timeline/js/timeline.js
changeset 65 03bbfd9cd3c6
child 66 37492d1ce841
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/timeline/js/timeline.js	Tue May 22 14:55:44 2012 +0200
@@ -0,0 +1,341 @@
+/*
+ * Main Timeline code
+ */
+
+window.Tlns = {
+    Utils : {},
+    Defaults : {},
+    Templates : {},
+    Classes : {}
+};
+
+/* Utility Functions */
+
+Tlns.Utils.SetDefaults = function(_object, _defaults, _options) {
+    var _options = _options || {};
+    _(_defaults).each(function(_v, _k) {
+        if(/^m(in|ax)_/.test(_k)) {
+            var _tab = _k.split('_')
+            if( typeof _object[_tab[1]] !== "undefined") {
+                var _fn = (_tab[0] === "min" ? Math.max : Math.min);
+                _object[_tab[1]] = _fn(_object[_tab[1]], _v);
+            }
+        } else {
+            if( typeof _options[_k] !== "undefined") {
+                _object[_k] = _options[_k];
+            } else {
+                _object[_k] = _v;
+            }
+        }
+    });
+}
+
+Tlns.Utils.dateFormat = function(_date, _template) {
+    if (typeof _data !== "object") {
+        _date = new Date(_date);
+    }
+    function zeroPad(_n) {
+        return (_n < 10 ? "0" : "") + _n
+    }
+    var _params = {
+        hours: _date.getHours(),
+        "0hours": zeroPad(_date.getHours()),
+        minutes: _date.getMinutes(),
+        "0minutes": zeroPad(_date.getMinutes()),
+        seconds: _date.getSeconds(),
+        "0seconds": zeroPad(_date.getSeconds()),
+        dayOfWeek: ["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"][_date.getDay()],
+        shortDayOfWeek: ["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"][_date.getDay()],
+        dayOfMonth: _date.getDate(),
+        "0dayOfMonth": zeroPad(_date.getDate()),
+        monthNumber: 1+_date.getMonth(),
+        "0monthNumber": zeroPad(1+_date.getMonth()),
+        monthName: ["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"][_date.getMonth()],
+        shortMonthName: ["jan","fev","mar","avr","mai","jun","jul","aou","sep","oct","nov","dec"][_date.getMonth()],
+        year: _date.getFullYear()
+    }
+    return Mustache.to_html(_template, _params);
+}
+
+/* Defaults */
+
+Tlns.Defaults.Timeline = {
+    container : "timeline",
+    width : 950,
+    height : 200,
+    url_univers : '',
+    min_width : 400,
+    min_height : 100,
+    main_width : 800,
+    timescales : [{
+        label : "Mois",
+        span : 32 * 86400 * 1000,
+        grid_interval : 5 * 86400 * 1000,
+        grid_date_format : '{{dayOfMonth}} {{shortMonthName}}',
+        start_date_format : '{{dayOfMonth}} {{shortMonthName}}',
+        end_date_format : '{{dayOfMonth}} {{shortMonthName}} {{year}}'
+    }, {
+        label : "Semaine",
+        span : 8 * 86400 * 1000,
+        grid_interval : 86400 * 1000,
+        grid_date_format : '{{shortDayOfWeek}} {{0dayOfMonth}}/{{0monthNumber}}',
+        start_date_format : '{{dayOfMonth}} {{shortMonthName}}',
+        end_date_format : '{{dayOfMonth}} {{shortMonthName}}'
+    }, {
+        label : "2 jours",
+        span : 2 * 86400 * 1000,
+        grid_interval : 8 * 3600 * 1000,
+        grid_date_format : '{{shortDayOfWeek}} {{0dayOfMonth}}/{{0monthNumber}} {{hours}}h',
+        start_date_format : '{{dayOfMonth}} {{shortMonthName}}',
+        end_date_format : '{{dayOfMonth}} {{shortMonthName}}'
+    }, {
+        label : "Demi-Journée",
+        span : 12 * 3600 * 1000,
+        grid_interval : 2 * 3600 * 1000,
+        grid_date_format : '{{hours}}h',
+        start_date_format : '{{dayOfMonth}} {{shortMonthName}} {{hours}}h',
+        end_date_format : '{{dayOfMonth}} {{shortMonthName}} {{hours}}h'
+    }, {
+        label : "3 Heures",
+        span : 3 * 3600 * 1000,
+        grid_interval : 30 * 60 * 1000,
+        grid_date_format : '{{0hours}}:{{0minutes}}',
+        start_date_format : '{{dayOfMonth}} {{shortMonthName}} {{0hours}}:{{0minutes}}',
+        end_date_format : '{{0hours}}:{{0minutes}}'
+    }, {
+        label : "1 Heure",
+        span : 80 * 60 * 1000,
+        grid_interval : 15 * 60 * 1000,
+        grid_date_format : '{{0hours}}:{{0minutes}}',
+        start_date_format : '{{dayOfMonth}} {{shortMonthName}} {{0hours}}:{{0minutes}}',
+        end_date_format : '{{0hours}}:{{0minutes}}'
+    }],
+    level: 0,
+    central_time: 0,
+    sync_now: true,
+    url_occurrences: '',
+    occurrences: {}
+}
+
+for (var _i = 0; _i < Tlns.Defaults.Timeline.timescales.length; _i++) {
+    Tlns.Defaults.Timeline.timescales[_i].level = _i;
+}
+
+/* Templates */
+
+Tlns.Templates.Timeline = '<div class="Tl-TopBar"><div class="Tl-TopBar-Button Tl-Border-Right"><div class="Tl-TopBar-AddButton"></div></div><div class="Tl-TopBar-Spacer Tl-Border-Right"></div>'
+    + '<div class="Tl-TopBar-Button Tl-Border-Right"><div class="Tl-TopBar-PreviousButton"></div></div><div class="Tl-TopBar-TimeSpan Tl-TopBar-TextBtn Tl-Border-Right">--/--</div>'
+    + '<div class="Tl-TopBar-Button Tl-Border-Right"><div class="Tl-TopBar-SyncButton"></div></div><div class="Tl-TopBar-Button Tl-Border-Right"><div class="Tl-TopBar-NextButton"></div></div><div class="Tl-TopBar-Spacer Tl-Border-Right"></div>'
+    + '<div class="Tl-TopBar-Timescales">{{#timescales}}<div class="Tl-TopBar-Button Tl-TopBar-TextBtn Tl-Border-Right" data-level="{{level}}">{{label}}</div>{{/timescales}}</div></div>'
+    + '<div class="Tl-BottomPart"><ul class="Tl-UniversLabels"></ul><div class="Tl-MainPart"><div class="Tl-Grid"></div></div></div>';
+
+Tlns.Templates.Univers = '<span class="Tl-UniversText">{{title}}</span>'
+
+/* Classes */
+
+Tlns.Classes.Timeline = function(_options) {
+
+    /* Setting Defaults */
+    Tlns.Utils.SetDefaults(this, Tlns.Defaults.Timeline, _options);
+
+    /* Setting container CSS */
+    this.$ = $('#' + this.container);
+    this.$.addClass('Tl-Main');
+    this.$.css({
+        width : this.width + "px",
+        height : this.height + "px"
+    });
+    this.$.html(Mustache.to_html(Tlns.Templates.Timeline, this));
+    
+    this.main_height = this.height - this.$.find('.Tl-TopBar').outerHeight();
+    this.$.find('.Tl-BottomPart').css("height", this.main_height + "px");
+    this.$.find('.Tl-MainPart').css("width", this.main_width + "px");
+    var _o = this.$.find('.Tl-MainPart').offset();
+    this.dragging_bounds = {
+        left: _o.left,
+        top: _o.top,
+        right: _o.left + this.$.find('.Tl-MainPart').outerWidth(),
+        bottom: _o.top + this.$.find('.Tl-MainPart').outerHeight(),
+    };
+    
+    this.setLevel(this.level);
+    
+    var _this = this;
+    this.$.find('.Tl-TopBar-Timescales>div').click(function() {
+        _this.setLevel($(this).attr("data-level"));
+    });
+    
+    this.$.find('.Tl-TopBar-SyncButton').click(function() {
+        _this.sync_now = !_this.sync_now;
+        _this.drawGrid();
+    })
+    
+    this.$.find('.Tl-TopBar-PreviousButton').click(function() {
+        _this.offsetTime(-_this.timescales[_this.level].span / 4);
+    });
+    
+    this.$.find('.Tl-TopBar-NextButton').click(function() {
+        _this.offsetTime(_this.timescales[_this.level].span / 4);
+    });
+    
+    this.$.find('.Tl-MainPart').mousedown(function(_event) {
+        _this.onMouseDown(_event);
+        return false;
+    });
+    
+    this.$.find('.Tl-MainPart').mousemove(function(_event) {
+        _this.onMouseMove(_event);
+        return false;
+    });
+    
+    this.$.find('.Tl-MainPart').mouseup(function(_event) {
+        _this.onMouseUp(_event);
+        return false;
+    });
+    
+    this.throttledSetTime = _.throttle(function(_time) {
+        _this.setTime(_time)
+    }, 150);
+    
+    /* Loading Univers */
+    $.getJSON(this.url_univers, function(_data) {
+        _this.onUniversLoaded(_data);
+    });
+}
+
+Tlns.Classes.Timeline.prototype.onMouseDown = function(_event) {
+    this.mouse_down = true;
+    this.is_dragging = false;
+    this.start_pos = {
+        x: _event.pageX,
+        y: _event.pageY
+    };
+    var _target = $(_event.target);
+    while (!_target.hasClass("Tl-Main") && _target.length) {
+        if (_target.hasClass("Tl-MainPart")) {
+            this.dragging_type = "timeline";
+            this.time_at_start = this.central_time;
+            break;
+        }
+        _target = _target.parent();
+    }
+}
+
+Tlns.Classes.Timeline.prototype.onMouseUp = function(_event) {
+    this.mouse_down = false;
+    this.is_dragging = false;
+}
+
+Tlns.Classes.Timeline.prototype.onMouseMove = function(_event) {
+    if (this.mouse_down) {
+        this.is_dragging = true;
+        if (_event.pageX > this.dragging_bounds.left
+            && _event.pageX < this.dragging_bounds.right
+            && _event.pageY > this.dragging_bounds.top
+            && _event.pageY < this.dragging_bounds.bottom) {
+            switch (this.dragging_type) {
+                case "timeline":
+                    var _newTime = Math.floor(this.time_at_start + ( this.start_pos.x - _event.pageX ) / this.current_scale);
+                    this.throttledSetTime(_newTime);
+                break;
+            }
+        } else {
+            this.onMouseUp(_event);
+        }
+    }
+}
+
+Tlns.Classes.Timeline.prototype.onUniversLoaded = function(_data) {
+    this.univers = [];
+    if(_data.length) {
+        this.univers_height = Math.floor(this.main_height / _data.length);
+    }
+    for(var _i = 0; _i < _data.length; _i++) {
+        this.univers.push(new Tlns.Classes.Univers(_data[_i], this, _i));
+    }
+}
+
+Tlns.Classes.Timeline.prototype.offsetTime = function(_timeOffset) {
+    this.setTime(this.central_time + _timeOffset);
+}
+
+Tlns.Classes.Timeline.prototype.setTime = function(_centralTime) {
+    this.sync_now = false;
+    this.central_time = _centralTime;
+    this.drawGrid();
+}
+
+Tlns.Classes.Timeline.prototype.setLevel = function(_level) {
+    if (_level >= 0 && _level < this.timescales.length) {
+        this.$.find('.Tl-TopBar-Timescales>div').each(function() {
+            var _el = $(this);
+            if (_el.attr("data-level") == _level) {
+                _el.addClass("active");
+            } else {
+                _el.removeClass("active");
+            }
+        });
+        this.level = _level;
+        this.drawGrid();
+    }
+}
+
+Tlns.Classes.Timeline.prototype.drawGrid = function() {
+    var _now = new Date().valueOf();
+    if (this.sync_now) {
+        this.central_time = _now;
+        this.$.find('.Tl-TopBar-SyncButton').addClass("active");
+    } else {
+        this.$.find('.Tl-TopBar-SyncButton').removeClass("active");
+    }
+    var _timescale = this.timescales[this.level],
+        _offset = new Date().getTimezoneOffset() * 60000;
+    this.current_scale = this.main_width / (_timescale.span);
+    var _tmin = this.central_time - (_timescale.span / 2),
+        _tmax = this.central_time + (_timescale.span / 2),
+        _grid_width = Math.floor(_timescale.grid_interval * this.current_scale),
+        _roundstart = Math.floor((_tmin - _offset) / _timescale.grid_interval) * _timescale.grid_interval + _offset,
+        _html = '';
+    this.$.find('.Tl-TopBar-TimeSpan').html(Tlns.Utils.dateFormat(_tmin, _timescale.start_date_format) + ' - ' + Tlns.Utils.dateFormat(_tmax, _timescale.end_date_format));
+    for (var _t = _roundstart; _t < _tmax; _t += _timescale.grid_interval) {
+        _html += '<div class="Tl-Grid-Column" style="width:' + _grid_width + 'px; left: ' + this.current_scale * (_t - this.central_time) + 'px">'
+        + '<div class="Tl-Grid-Label">' + Tlns.Utils.dateFormat(_t, _timescale.grid_date_format) + '</div></div>';
+    }
+/*
+ 
+    for (var _t = _roundstart; _t < _tmax; _t += _timescale.grid_interval) {
+        var _isMajor = !(Math.floor((_t - _offset) / _timescale.grid_interval) % _timescale.major_interval);
+        _html += '<div class="Tl-Grid-Column' + ( _isMajor ? ' Tl-Grid-Major' : '' ) + '" style="width:' + _grid_width + 'px; left: ' + _scale * (_t - this.central_time) + 'px">'
+        + ( _isMajor ? '<div class="Tl-Grid-Label">' + Tlns.Utils.dateFormat(_t, _timescale.date_format) + '</div>' : '' ) + '</div>';
+    }
+*/
+    if (_tmin <= _now && _tmax >= _now) {
+        _html += '<div class="Tl-Grid-Now" style="left: ' + this.current_scale * (_now - this.central_time) + 'px"></div>'
+    }
+    this.$.find('.Tl-Grid').html(_html);
+}
+
+Tlns.Classes.Timeline.prototype.drawOccurrences = function() {
+    
+}
+
+Tlns.Classes.Univers = function(_data, _timeline, _index) {
+    this.id = _data.id;
+    this.index = _index;
+    this.title = _data.nom;
+    this.mainCharacter = _data.personnage;
+
+    this.$label = $('<li>').css({
+        height : _timeline.univers_height + "px"
+    }).html(Mustache.to_html(Tlns.Templates.Univers, this))
+    .addClass((_index % 2) ? 'Tl-Line-Odd' : 'Tl-Line-Even');
+    
+    _timeline.$.find('.Tl-UniversLabels').append(this.$label);
+    var _txt = _data.nom,
+        _span = this.$label.find('span');
+
+    while (_span.outerWidth() > (_timeline.width - _timeline.main_width) && _txt) {
+        _txt = _txt.substr(0, _txt.length - 1);
+        _span.html(_txt + '&hellip;');
+    }
+}