AngularJS Live Example

by ncapito

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script src="https://code.angularjs.org/1.5.8/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="MyController">

    <timetable></timetable>

  </div>

</div>

CSS

.timetable{box-sizing:border-box}.timetable::after{clear:both;content:"";display:table}.timetable *,.timetable *:before,.timetable *:after{box-sizing:inherit}.timetable ul,.timetable li{margin:0;padding:0;list-style-type:none}.timetable aside,.timetable section{float:left}.timetable aside{width:25%;padding:0!important;margin-top:46px;border-right:5px solid transparent}.timetable aside li{padding:0 15px;background-color:#EFEFEF;line-height:46px}.timetable aside li:not(:last-of-type){border-bottom:1px solid #fff}.timetable aside .row-heading{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal}.timetable ul{list-style-type:none;margin:0}.timetable aside li,.timetable time li{height:46px}.timetable section{width:75%;padding:0!important;overflow-x:scroll;-webkit-overflow-scrolling:touch}.timetable section time{white-space:nowrap;display:inline-block}.timetable section header{height:46px;transform-style:preserve-3d;font-size:0}.timetable section header::after{clear:both;content:"";display:table}.timetable section header li{display:inline-block;font-size:1rem;overflow:visible;width:0;line-height:46px;display:inline-block;position:relative}.timetable section header li:not(:last-of-type){width:96px}.timetable section header li .time-label{display:block;position:absolute;left:0}.timetable section header li:not(:first-of-type) .time-label{transform:translateX(-50%)}.timetable section header li:last-of-type .time-label{transform:translateX(-100%)}@media only screen and (max-width:960px){.timetable aside{width:40%}.timetable section{width:60%}}.timetable ul.room-timeline{border-left:none;position:relative;overflow:hidden}.timetable ul.room-timeline li{position:relative;background-color:#F4F4F4}.timetable ul.room-timeline li:nth-of-type(odd){background-color:#FDFDFD}.timetable ul.room-timeline li:first-of-type{border-top:1px solid #E5E5E5}.timetable ul.room-timeline li:last-of-type{border-bottom:1px solid...

JavaScript

var Timetable=function(){this.scope={hourStart:9,hourEnd:17},this.locations=[],this.events=[]};Timetable.Renderer=function(t){if(!(t instanceof Timetable))throw new Error("Initialize renderer using a Timetable");this.timetable=t},function(){function t(t,n){return e(t)&&e(n)}function e(t){return n(t)&&o(t)}function n(t){return t===parseInt(t,10)}function o(t){return t>=0&&t<24}function r(t,e){return e.indexOf(t)!==-1}function i(t,e){var n=t instanceof Date&&e instanceof Date,o=t<e;return n&&o}function a(t,e){return e>=t?e-t:24+e-t}function c(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function s(t){var e=t<10?"0":"";return e+t+":00"}Timetable.prototype={setScope:function(e,n){if(!t(e,n))throw new RangeError("Timetable scope should consist of (start, end) in whole hours from 0 to 23");return this.scope.hourStart=e,this.scope.hourEnd=n,this},addLocations:function(t){function e(){return t instanceof Array}var n=this.locations;if(!e())throw new Error("Tried to add locations in wrong format");return t.forEach(function(t){if(r(t,n))throw new Error("Location already exists");n.push(t)}),this},addEvent:function(t,e,n,o,a){if(!r(e,this.locations))throw new Error("Unknown location");if(!i(n,o))throw new Error("Invalid time range: "+JSON.stringify([n,o]));var c="[object Object]"===Object.prototype.toString.call(a);return this.events.push({name:t,location:e,startDate:n,endDate:o,options:c?a:void 0}),this}},Timetable.Renderer.prototype={draw:function(t){function e(t){if(null===t)throw new Error("Timetable container not found")}function n(t){var e=t.appendChild(document.createElement("aside")),n=e.appendChild(document.createElement("ul"));o(n)}function o(t){for(var e=0;e<f.locations.length;e++){var n=t.appendChild(document.createElement("li")),o=n.appendChild(document.createElement("span"));o.className="row-heading",o.textContent=f.locations[e]}}function r(t){var...