timeline/js/timeline.js
changeset 80 00a3532ad8cf
parent 79 be3defb1bbdb
child 81 bf6adf981cc2
equal deleted inserted replaced
79:be3defb1bbdb 80:00a3532ad8cf
     1 /*
       
     2  * Main Timeline code
       
     3  */
       
     4 
       
     5 window.Tlns = {
       
     6     Utils : {},
       
     7     Defaults : {},
       
     8     Templates : {},
       
     9     Classes : {}
       
    10 };
       
    11 
       
    12 /* Utility Functions */
       
    13 
       
    14 Tlns.Utils.zeroPad = function(_n) {
       
    15     return (_n < 10 ? "0" : "") + _n;
       
    16 }
       
    17 
       
    18 Tlns.Utils.SetDefaults = function(_object, _defaults, _options) {
       
    19     var _options = _options || {};
       
    20     _(_defaults).each(function(_v, _k) {
       
    21         if(/^m(in|ax)_/.test(_k)) {
       
    22             var _tab = _k.split('_')
       
    23             if( typeof _object[_tab[1]] !== "undefined") {
       
    24                 var _fn = (_tab[0] === "min" ? Math.max : Math.min);
       
    25                 _object[_tab[1]] = _fn(_object[_tab[1]], _v);
       
    26             }
       
    27         } else {
       
    28             if( typeof _options[_k] !== "undefined") {
       
    29                 _object[_k] = _options[_k];
       
    30             } else {
       
    31                 _object[_k] = _v;
       
    32             }
       
    33         }
       
    34     });
       
    35 }
       
    36 
       
    37 Tlns.Utils.dateFormat = function(_date, _template) {
       
    38     if (typeof _data !== "object") {
       
    39         _date = new Date(_date);
       
    40     }
       
    41     var _params = {
       
    42         hours: _date.getHours(),
       
    43         "0hours": Tlns.Utils.zeroPad(_date.getHours()),
       
    44         minutes: _date.getMinutes(),
       
    45         "0minutes": Tlns.Utils.zeroPad(_date.getMinutes()),
       
    46         seconds: _date.getSeconds(),
       
    47         "0seconds": Tlns.Utils.zeroPad(_date.getSeconds()),
       
    48         dayOfWeek: ["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"][_date.getDay()],
       
    49         shortDayOfWeek: ["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"][_date.getDay()],
       
    50         dayOfMonth: _date.getDate(),
       
    51         "0dayOfMonth": Tlns.Utils.zeroPad(_date.getDate()),
       
    52         monthNumber: 1+_date.getMonth(),
       
    53         "0monthNumber": Tlns.Utils.zeroPad(1+_date.getMonth()),
       
    54         monthName: ["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"][_date.getMonth()],
       
    55         shortMonthName: ["jan","fev","mar","avr","mai","jun","jul","aou","sep","oct","nov","dec"][_date.getMonth()],
       
    56         year: _date.getFullYear()
       
    57     }
       
    58     return Mustache.to_html(_template, _params);
       
    59 }
       
    60 
       
    61 Tlns.Utils.guid = function() {
       
    62     return 'xxxx-xxxx-xxxx-xxxx'.replace(/x/g,function() {
       
    63         return Math.floor(Math.random()*16).toString(16);
       
    64     });
       
    65 }
       
    66 
       
    67 Tlns.Utils.drawArrow = function(_ctx, _color, _x1, _y1, _x2, _y2) {
       
    68     _ctx.strokeStyle = _color;
       
    69     _ctx.fillStyle = _color;
       
    70     _ctx.beginPath();
       
    71     _ctx.moveTo(_x1,_y1);
       
    72     _ctx.lineTo(_x2,_y2);
       
    73     _ctx.stroke();
       
    74     var _mod = Math.sqrt(Math.pow(_x2 - _x1, 2) + Math.pow(_y2 - _y1, 2)),
       
    75         _xu = (_x2 - _x1) / _mod,
       
    76         _yu = (_y2 - _y1) / _mod,
       
    77         _xm = (_x1 + _x2) / 2,
       
    78         _ym = (_y1 + _y2) / 2,
       
    79         _arrowWidth = 4,
       
    80         _arrowLength = 8,
       
    81         _x3 = _xm - _arrowLength * _xu + _arrowWidth * _yu,
       
    82         _y3 = _ym - _arrowLength * _yu - _arrowWidth * _xu,
       
    83         _x4 = _xm - _arrowLength * _xu - _arrowWidth * _yu,
       
    84         _y4 = _ym - _arrowLength * _yu + _arrowWidth * _xu;
       
    85     _ctx.beginPath();
       
    86     _ctx.moveTo(_x3, _y3);
       
    87     _ctx.lineTo(_xm, _ym);
       
    88     _ctx.lineTo(_x4, _y4);
       
    89     _ctx.fill();
       
    90     _ctx.stroke();
       
    91 }
       
    92 
       
    93 Tlns.Utils.timeFieldProcess = function(_val) {
       
    94     var _h = 0,
       
    95         _m = 0,
       
    96         _matches = _val.match(/(\d+)/g);
       
    97     if (_matches && _matches.length) {
       
    98         _h = Math.min(23, +(_matches[0]));
       
    99         if (_matches.length > 1) {
       
   100             _m = Math.min(59, +(_matches[1]));
       
   101         }
       
   102     }
       
   103     return {
       
   104         hours: _h,
       
   105         minutes: _m,
       
   106         text: Tlns.Utils.zeroPad(_h) + ':' + Tlns.Utils.zeroPad(_m)
       
   107     }
       
   108 }
       
   109 
       
   110 Tlns.Utils.dateFieldProcess = function(_val) {
       
   111     var _now = new Date(),
       
   112         _y = _now.getFullYear(),
       
   113         _m = 1 + _now.getMonth(),
       
   114         _d = _now.getDate(),
       
   115         _matches = _val.match(/(\d+)/g);
       
   116     if (_matches && _matches.length) {
       
   117         _d = Math.min(31, +(_matches[0]));
       
   118         if (_matches.length > 1) {
       
   119             _m = Math.min(12, +(_matches[1]));
       
   120         }
       
   121         if (_matches.length > 2) {
       
   122             _y = parseInt(_matches[2]);
       
   123             if (_y < 2000) {
       
   124                 _y += 2000;
       
   125             }
       
   126             _y = Math.min(2020, Math.max(2000, _y));
       
   127         }
       
   128     }
       
   129     return {
       
   130         year: _y,
       
   131         month: _m,
       
   132         date: _d,
       
   133         text: Tlns.Utils.zeroPad(_d) + '/' + Tlns.Utils.zeroPad(_m) + '/' + _y
       
   134     }
       
   135 }
       
   136 
       
   137 /* Defaults */
       
   138 
       
   139 Tlns.Defaults.Timeline = {
       
   140     container : "timeline",
       
   141     width : 950,
       
   142     height : 200,
       
   143     url_univers : '',
       
   144     min_width : 400,
       
   145     min_height : 100,
       
   146     main_width : 800,
       
   147     timescales : [{
       
   148         label : "Mois",
       
   149         span : 32 * 86400 * 1000,
       
   150         grid_interval : 5 * 86400 * 1000,
       
   151         grid_date_format : '{{dayOfMonth}} {{shortMonthName}}',
       
   152         start_date_format : '{{dayOfMonth}} {{shortMonthName}}',
       
   153         end_date_format : '{{dayOfMonth}} {{shortMonthName}} {{year}}'
       
   154     }, {
       
   155         label : "Semaine",
       
   156         span : 8 * 86400 * 1000,
       
   157         grid_interval : 86400 * 1000,
       
   158         grid_date_format : '{{shortDayOfWeek}} {{0dayOfMonth}}/{{0monthNumber}}',
       
   159         start_date_format : '{{dayOfMonth}} {{shortMonthName}}',
       
   160         end_date_format : '{{dayOfMonth}} {{shortMonthName}}'
       
   161     }, {
       
   162         label : "2 jours",
       
   163         span : 2 * 86400 * 1000,
       
   164         grid_interval : 8 * 3600 * 1000,
       
   165         grid_date_format : '{{shortDayOfWeek}} {{0dayOfMonth}}/{{0monthNumber}} {{hours}}h',
       
   166         start_date_format : '{{dayOfMonth}} {{shortMonthName}}',
       
   167         end_date_format : '{{dayOfMonth}} {{shortMonthName}}'
       
   168     }, {
       
   169         label : "Demi-Journée",
       
   170         span : 12 * 3600 * 1000,
       
   171         grid_interval : 2 * 3600 * 1000,
       
   172         grid_date_format : '{{hours}}h',
       
   173         start_date_format : '{{dayOfMonth}} {{shortMonthName}} {{hours}}h',
       
   174         end_date_format : '{{dayOfMonth}} {{shortMonthName}} {{hours}}h'
       
   175     }, {
       
   176         label : "3 Heures",
       
   177         span : 3 * 3600 * 1000,
       
   178         grid_interval : 30 * 60 * 1000,
       
   179         grid_date_format : '{{0hours}}:{{0minutes}}',
       
   180         start_date_format : '{{dayOfMonth}} {{shortMonthName}} {{0hours}}:{{0minutes}}',
       
   181         end_date_format : '{{0hours}}:{{0minutes}}'
       
   182     }, {
       
   183         label : "1 Heure",
       
   184         span : 60 * 60 * 1000,
       
   185         grid_interval : 15 * 60 * 1000,
       
   186         grid_date_format : '{{0hours}}:{{0minutes}}',
       
   187         start_date_format : '{{dayOfMonth}} {{shortMonthName}} {{0hours}}:{{0minutes}}',
       
   188         end_date_format : '{{0hours}}:{{0minutes}}'
       
   189     }],
       
   190     level: 0,
       
   191     central_time: 0,
       
   192     sync_now: true,
       
   193     urls_occurrences: [],
       
   194     occurrences: [],
       
   195     cluster_spacing: 12,
       
   196     tooltip_date_format: '{{dayOfMonth}} {{shortMonthName}} {{year}} {{0hours}}:{{0minutes}}',
       
   197     statuses: {
       
   198         "valide": "Validée",
       
   199         "a_valider": "A valider",
       
   200         "a_realiser": "A réaliser"
       
   201     }
       
   202 }
       
   203 
       
   204 for (var _i = 0; _i < Tlns.Defaults.Timeline.timescales.length; _i++) {
       
   205     Tlns.Defaults.Timeline.timescales[_i].level = _i;
       
   206 }
       
   207 
       
   208 /* Templates */
       
   209 
       
   210 Tlns.Templates.Timeline = '<ul class="Onglets"><li class="Onglet-Tl active">Frise chronologique</li><li class="Onglet-Ls">Liste des occurrences</li></ul><div class="Tl-Main"><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>'
       
   211     + '<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>'
       
   212     + '<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>'
       
   213     + '<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>'
       
   214     + '<div class="Tl-BottomPart"><ul class="Tl-UniversLabels"></ul>'
       
   215     + '<div class="Tl-MainPart"><div class="Tl-Layer Tl-Grid"></div><canvas class="Tl-Layer Tl-Canvas"></canvas><canvas class="Tl-Layer Tl-Linking-Canvas"></canvas><div class="Tl-Layer Tl-Occurrences"></div>'
       
   216     + '<ul class="Tl-Adding"><li class="Tl-AddingTitle">Ajout d\'une occurrence</li><li><span>Narrative</span><div class="Tl-AddOccurrence Tl-Occnarrative" occurrence-type="narrative" title="Glisser sur la frise chronologique pour ajouter"></div></li>'
       
   217     + '<li><span>De Publication</span><div class="Tl-AddOccurrence Tl-Occpublication" occurrence-type="publication" title="Glisser sur la frise chronologique pour ajouter"></div></li></ul></div>'
       
   218     + '<div class="Tl-Overlay-Container"><div class="Tl-Overlay-Box"><div class="Tl-Overlay"><div class="Tl-Overlay-Tip-Top"></div><div class="Tl-Overlay-Main"></div><div class="Tl-Overlay-Tip-Bottom"></div></div></div></div></div></div>'
       
   219     
       
   220     +'<div class="Ls-Main"><div class="Ls-Filtres"><h2>Filtres&nbsp;:</h2>'
       
   221     + '<div class="Ls-Column"><h3>Univers&nbsp;:</h3><ul class="Ls-Univers"></ul></div>'
       
   222     + '<div class="Ls-Column"><h3>Type d\'occurrence&nbsp;:</h3><ul class="Ls-Occtypes"><li data="narrative" class="Ls-Critere Ls-Active Ls-CrWithIcon"><div class="Ls-OccIcon Tl-Occnarrative"></div>Narratives</li><li data="publication" class="Ls-Critere Ls-Active Ls-CrWithIcon"><div class="Ls-OccIcon Tl-Occpublication"></div>de Publication</li></ul>'
       
   223     + '<h3>Statut&nbsp;:</h3><ul class="Ls-Occstatuses"><li data="a_realiser" class="Ls-Critere Ls-Active Ls-CrWithIcon"><div class="Ls-OccIcon Tl-Occpublication Tl-Occa_realiser"></div>À réaliser</li><li data="a_valider" class="Ls-Critere Ls-Active Ls-CrWithIcon"><div class="Ls-OccIcon Tl-Occpublication Tl-Occa_valider"></div>À valider</li><li data="valide" class="Ls-Critere Ls-Active Ls-CrWithIcon"><div class="Ls-OccIcon Tl-Occpublication Tl-Occvalide"></div>Validé</li></ul>'
       
   224     + '<h3>Est au JT&nbsp;:</h3><ul class="Ls-IsJt"><li class="Ls-Critere Ls-Active" data="1">Oui</li><li class="Ls-Critere Ls-Active" data="0">Non</li></ul></div>'
       
   225     + '<div class="Ls-Column"><h3>Recherche par titre&nbsp;:</h3><p><input class="Ls-Search" type="search" placeholder="Rechercher" /></p><h3>Date&nbsp;:</h3><p><label class="Ls-Label">Du </label><input class="Ls-From-Date Ls-Input" /></p><p><label class="Ls-Label">à </label><input class="Ls-From-Time Ls-Input" /></p><p><label class="Ls-Label">Au </label><input class="Ls-To-Date Ls-Input" /></p><p><label class="Ls-Label">à </label><input class="Ls-To-Time Ls-Input" /></p></div>'
       
   226     + '</div><div class="Ls-Resultats"><h2>Occurrences&nbsp;:</h2><ul class="Ls-Occurrences"></ul></div></div>';
       
   227 
       
   228 Tlns.Templates.Univers = '<span class="Tl-UniversText">{{title}}</span>';
       
   229 
       
   230 Tlns.Templates.Univers_List = '{{#univers}}<li data="{{id}}" class="Ls-Critere Ls-Active">{{title}}</li>{{/univers}}';
       
   231 
       
   232 Tlns.Templates.Occurrence = '{{#clusters}}<div class="Tl-Cluster" style="left: {{x}}px; top: {{y}}px;" cluster-contents="{{contents}}">'
       
   233     + '<div class="Tl-ClusterCount">{{occurrences.length}}</div></div>{{/clusters}}'
       
   234     + '{{#occurrences}}<div class="Tl-Occurrence Tl-OccOnGrid Tl-Occ{{type}} Tl-Occ{{status}}{{#editing}} Tl-Editing{{/editing}}" occurrence-id="{{id}}" style="left: {{x}}px; top: {{y}}px;">'
       
   235 //  + '{{#locked}}<div class="Tl-Locked"></div>{{/locked}}'
       
   236     + '<div class="Tl-Link"{{#editing}} style="display: block"{{/editing}}></div></div>{{/occurrences}}{{#open_cluster}}<div class="Tl-ClusterOverlay" style="left: {{x}}px; top: {{y}}px;">'
       
   237     + '{{#occurrences}}<div class="Tl-Occurrence Tl-OccInCluster Tl-Occ{{type}} Tl-Occ{{status}}{{#editing}} Tl-Editing{{/editing}}" occurrence-id="{{id}}">'
       
   238     + '{{#locked}}<div class="Tl-Locked"></div>{{/locked}}<div class="Tl-Link"{{#editing}} style="display: block"{{/editing}}></div></div>{{/occurrences}}</div>{{/open_cluster}}';
       
   239 
       
   240 Tlns.Templates.Occurrence_List = '{{#occurrences}}<li class="Ls-Occurrence"><div class="Ls-OccIcon Tl-Occ{{type}} Tl-Occ{{status}}"></div><h4 class="Ls-Occurrence-Title">{{title}}</h4>'
       
   241     + '<p class="Ls-Occ-More">{{formatted_date}} &mdash; {{univers.title}} &mdash; {{translated_status}} &mdash; {{#jt}}Au JT{{/jt}}{{^jt}}Hors JT{{/jt}}</p><div style="clear:both;"></div></li>{{/occurrences}}';
       
   242 
       
   243 Tlns.Templates.OccurrenceTooltip = '<h3 class="Tl-Tooltip-Title">{{title}}</h3><p class="Tl-Tooltip-Date">{{formatted_date}} &mdash; {{translated_status}}</p>'
       
   244     + '<p class="Tl-Tooltip-Description">{{description}}</p>'
       
   245 //    + '<p class="Tl-Tooltip-Characters">{{univers.mainCharacter}}{{#characters}}, {{.}}{{/characters}}</p>'
       
   246 
       
   247 /* Classes */
       
   248 
       
   249 Tlns.Classes.Timeline = function(_options) {
       
   250 
       
   251     /* Setting Defaults */
       
   252     Tlns.Utils.SetDefaults(this, Tlns.Defaults.Timeline, _options);
       
   253 
       
   254     /* Setting container CSS */
       
   255     this.$ = $('#' + this.container).html(Mustache.to_html(Tlns.Templates.Timeline, this));
       
   256     
       
   257     this.$.find('.Tl-Main').css({
       
   258         width : this.width + "px",
       
   259         height : this.height + "px"
       
   260     });
       
   261     this.main_height = this.height - this.$.find('.Tl-TopBar').outerHeight();
       
   262     this.$.find('.Tl-BottomPart').css("height", this.main_height + "px");
       
   263     this.$.find('.Tl-MainPart').css("width", this.main_width + "px");
       
   264     this.$.find('.Tl-Overlay-Container').css("left", (this.$.find('.Tl-BottomPart').outerWidth() - this.main_width) + "px");
       
   265     this.$.find('canvas.Tl-Layer').attr({
       
   266         width: this.main_width,
       
   267         height: this.main_height
       
   268     });
       
   269     var _o = this.$.find('.Tl-MainPart').offset();
       
   270     this.dragging_bounds = {
       
   271         left: _o.left,
       
   272         top: _o.top,
       
   273         right: _o.left + this.$.find('.Tl-MainPart').outerWidth(),
       
   274         bottom: _o.top + this.$.find('.Tl-MainPart').outerHeight(),
       
   275     };
       
   276     
       
   277     var _this = this;
       
   278     
       
   279     this.throttledDrawGrid = _.throttle(function() {
       
   280         _this.drawGrid();
       
   281     }, 150);
       
   282     
       
   283     this.throttledDrawList = _.throttle(function() {
       
   284         _this.drawList();
       
   285     }, 150);
       
   286     
       
   287     this.setLevel(this.level);
       
   288     
       
   289     this.$.find('.Tl-TopBar-Timescales>div').click(function() {
       
   290         _this.setLevel($(this).attr("data-level"));
       
   291     });
       
   292     
       
   293     this.$.find('.Tl-TopBar-SyncButton').click(function() {
       
   294         _this.sync_now = !_this.sync_now;
       
   295         _this.changeSpan();
       
   296     })
       
   297     
       
   298     this.$.find('.Tl-TopBar-PreviousButton').click(function() {
       
   299         _this.offsetTime(-_this.timescales[_this.level].span / 4);
       
   300     });
       
   301     
       
   302     this.$.find('.Tl-TopBar-NextButton').click(function() {
       
   303         _this.offsetTime(_this.timescales[_this.level].span / 4);
       
   304     });
       
   305     
       
   306     this.$.find('.Tl-MainPart').mousedown(function(_event) {
       
   307         _this.onMouseDown(_event);
       
   308         return false;
       
   309     });
       
   310     
       
   311     this.$.find('.Tl-MainPart').mousemove(function(_event) {
       
   312         _this.onMouseMove(_event);
       
   313         return false;
       
   314     });
       
   315     
       
   316     this.$.find('.Tl-MainPart').mouseup(function(_event) {
       
   317         _this.onMouseUp(_event);
       
   318         return false;
       
   319     });
       
   320     
       
   321     this.$.find('.Tl-MainPart').mousewheel(function(_event, _delta) {
       
   322         var _newLevel = Math.max(0,Math.min(_this.timescales.length-1, (_delta < 0 ? -1 : 1) + parseInt(_this.level)));
       
   323         if (_newLevel != _this.level) {
       
   324             _this.hideTooltip();
       
   325             var _deltaX = _event.pageX - _this.dragging_bounds.left,
       
   326                 _tAtMouse = _this.timeFromMouse(_event.pageX),
       
   327                 _newScale = _this.main_width / (_this.timescales[_newLevel].span),
       
   328                 _newStart = _tAtMouse - _deltaX / _newScale;
       
   329             _this.central_time = _newStart + _this.timescales[_newLevel].span / 2;
       
   330             _this.setLevel(_newLevel);
       
   331         }
       
   332         return false;
       
   333     });
       
   334     
       
   335     this.$.find('.Tl-Overlay-Box').mouseover(function(_event) {
       
   336         $(this).show();
       
   337     }).mouseout(function(_event) {
       
   338         $(this).hide();
       
   339     });
       
   340     
       
   341     this.$.find('.Tl-TopBar-AddButton').click(function() {
       
   342         $(this).toggleClass('active');
       
   343         _this.$.find('.Tl-Adding').toggle();
       
   344     });
       
   345     
       
   346     this.$.find('.Tl-AddOccurrence').mousedown(function(_event) {
       
   347         var _el = $(this),
       
   348             _type = _el.attr("occurrence-type"),
       
   349             _d = _this.timeFromMouse(_event.pageX),
       
   350             _u = _this.universFromMouse(_event.pageY),
       
   351             _occ = _this.createOrUpdateOccurrence(
       
   352                 _type,
       
   353                 {
       
   354                     datePublication: Math.floor(_d / 1000),
       
   355                     titre: '<Nouvelle occurrence>',
       
   356                     idUnivers: _this.univers[_u].id,
       
   357                     statut: 'a_realiser',
       
   358                     jt: false
       
   359                 }
       
   360             );
       
   361         _occ.just_created = true;
       
   362         _occ.editing = true;
       
   363         _this.editing_occurrence = _occ;
       
   364         _this.dragging_type = "occurrence";
       
   365         window.setTimeout(function () {
       
   366             _this.$.find('.Tl-TopBar-AddButton').removeClass('active');
       
   367             _this.$.find('.Tl-Adding').hide();
       
   368         }, 200);
       
   369         _this.throttledDrawGrid();
       
   370     }).mouseup(function(_event) {
       
   371         _this.onMouseUp(_event);
       
   372         return false;
       
   373     });
       
   374     
       
   375     /* Loading Univers */
       
   376     $.getJSON(this.url_univers, function(_data) {
       
   377         _this.onUniversLoaded(_data);
       
   378     });
       
   379     
       
   380     /* LIST */
       
   381     
       
   382     this.$.find("li.Ls-Critere").click(function() {
       
   383         $(this).toggleClass("Ls-Active");
       
   384         _this.throttledDrawList();
       
   385     });
       
   386     this.$.find(".Ls-Search").bind("keyup change click", function() {
       
   387         _this.throttledDrawList();
       
   388     });
       
   389     this.$.find(".Ls-From-Date, .Ls-To-Date").datepicker(
       
   390         {
       
   391             dateFormat: "dd/mm/yy",
       
   392             dayNames: [ "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi" ],
       
   393             dayNamesShort: [ "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam" ],
       
   394             dayNamesMin: [ "D", "L", "Ma", "Me", "J", "V", "S" ],
       
   395             monthNames:  [ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre" ],
       
   396             monthNamesShort: [ "Jan", "Fév", "Mar", "Avr", "Mai", "Jun", "Jul", "Aoû", "Sep", "Oct", "Nov", "Déc" ],
       
   397             showOtherMonths: true,
       
   398             selectOtherMonths: true
       
   399         }
       
   400     ).change(function() {
       
   401         var _val = $(this).val();
       
   402         if (_val) {
       
   403             $(this).val(Tlns.Utils.dateFieldProcess( _val ).text);
       
   404         }
       
   405         _this.drawList();
       
   406     }).bind("keyup", function() {
       
   407         _this.throttledDrawList();
       
   408     });
       
   409     this.$.find(".Ls-From-Time, .Ls-To-Time").change(function() {
       
   410         var _val = $(this).val();
       
   411         if (_val) {
       
   412             $(this).val(Tlns.Utils.timeFieldProcess( _val ).text);
       
   413         }
       
   414         _this.throttledDrawList();
       
   415     }).bind("keyup", function() {
       
   416         _this.throttledDrawList();
       
   417     });
       
   418     
       
   419     this.$.find(".Onglet-Tl").click(function() {
       
   420         _this.$.find(".Tl-Main").show();
       
   421         _this.$.find(".Ls-Main").hide();
       
   422         _this.$.find(".Onglet-Ls").removeClass("active");
       
   423         _this.$.find(".Onglet-Tl").addClass("active");
       
   424         _this.throttledDrawGrid();
       
   425     });
       
   426     
       
   427     this.$.find(".Onglet-Ls").click(function() {
       
   428         _this.$.find(".Ls-Main").show();
       
   429         _this.$.find(".Tl-Main").hide();
       
   430         _this.$.find(".Onglet-Tl").removeClass("active");
       
   431         _this.$.find(".Onglet-Ls").addClass("active");
       
   432         _this.throttledDrawList();
       
   433     });
       
   434     
       
   435     
       
   436     /* BINDING MEDIADATA EVENTS */
       
   437     $("body").bind("AjoutOccurrenceMediadata MiseAJourOccurrenceMediadata SuppressionOccurrenceMediadata AjoutDependanceMediadata SuppressionDependanceMediadata AjoutDependanceTimeline AjoutOccurrenceTimeline MiseAJourOccurrenceTimeline", function(_event, _data) {
       
   438         console.log(_event.type + " called with data " + JSON.stringify(_data));
       
   439     });
       
   440    
       
   441     $("body").bind("AjoutOccurrenceMediadata MiseAJourOccurrenceMediadata", function(_event, _data) {
       
   442         var _type = _data.typeOccurrence.replace(/^occurrence/i,'').toLowerCase(),
       
   443             _contents = _({ id: _data.id }).extend(_data.contenu);
       
   444         _this.createOrUpdateOccurrence(_type, _contents);
       
   445         _this.throttledDrawGrid();
       
   446     });
       
   447     
       
   448     $("body").bind("SuppressionOccurrenceMediadata", function(_event, _data) {
       
   449         var _id = _data.typeOccurrence.replace(/^occurrence/i,'').toLowerCase() + '_' + _data.id;
       
   450         _this.deleteOccurrence(_id);
       
   451         _this.throttledDrawGrid();
       
   452     });
       
   453     
       
   454     $("body").bind("AjoutDependanceMediadata", function(_event, _data) {
       
   455         var _sourceId = _data.typeOccurrence.replace(/^occurrence/i,'').toLowerCase() + '_' + _data.id,
       
   456             _targetId = _data.typeOccurrenceCible.replace(/^occurrence/i,'').toLowerCase() + '_' + _data.idCible;
       
   457         _this.getOccurrence(_sourceId).addDependency(_targetId);
       
   458         _this.throttledDrawGrid();
       
   459     });
       
   460     
       
   461     $("body").bind("SuppressionDependanceMediadata", function(_event, _data) {
       
   462         var _sourceId = _data.typeOccurrence.replace(/^occurrence/i,'').toLowerCase() + '_' + _data.id,
       
   463             _targetId = _data.typeOccurrenceCible.replace(/^occurrence/i,'').toLowerCase() + '_' + _data.idCible;
       
   464         _this.getOccurrence(_sourceId).addDependency(_targetId);
       
   465         _this.throttledDrawGrid();
       
   466     });
       
   467     
       
   468     
       
   469 }
       
   470 
       
   471 Tlns.Classes.Timeline.prototype.onMouseDown = function(_event) {
       
   472     this.mouse_down = true;
       
   473     this.is_dragging = false;
       
   474     this.start_pos = {
       
   475         x: _event.pageX,
       
   476         y: _event.pageY
       
   477     };
       
   478     if (typeof this.dragging_type === "undefined") {
       
   479         this.time_at_start = this.central_time;
       
   480         this.dragging_type = "timeline";
       
   481     }
       
   482 }
       
   483 
       
   484 Tlns.Classes.Timeline.prototype.onMouseUp = function(_event) {
       
   485     if (this.is_dragging) {
       
   486         switch (this.dragging_type) {
       
   487             case "occurrence":
       
   488                 var _event = ( this.editing_occurrence.just_created ? "Ajout" : "MiseAJour" ) + "OccurrenceTimeline",
       
   489                     _data = {
       
   490                         id: this.editing_occurrence.original_id,
       
   491                         typeOccurrence: "occurrence" + this.editing_occurrence.type.replace(/^./,function(_l) { return _l.toUpperCase()}),
       
   492                         contenu: {
       
   493                             datePublication : Math.floor(this.editing_occurrence.date / 1000),
       
   494                             titre : this.editing_occurrence.title,
       
   495                             idUnivers: this.editing_occurrence.univers_id,
       
   496                             statut: this.statuses[this.editing_occurrence.status],
       
   497                             JT: +!!this.editing_occurrence.jt
       
   498                         }
       
   499                     }
       
   500                 $("body").trigger(_event, _data);
       
   501                 this.editing_occurrence.editing = false;
       
   502                 this.editing_occurrence.just_created = false;
       
   503                 this.throttledDrawGrid();
       
   504             break;
       
   505             case "link":
       
   506                 this.editing_occurrence.editing = false;
       
   507                 this.throttledDrawGrid();
       
   508                 var _ctx = this.$.find('.Tl-Linking-Canvas')[0].getContext('2d');
       
   509                 _ctx.clearRect(0,0,this.main_width, this.main_height);
       
   510             break;
       
   511         }
       
   512     } else {
       
   513         if (this.dragging_type == "occurrence" && this.editing_occurrence.just_created) {
       
   514             this.deleteOccurrence(this.editing_occurrence.id);
       
   515             this.throttledDrawGrid();
       
   516         }
       
   517     }
       
   518     this.mouse_down = false;
       
   519     this.is_dragging = false;
       
   520     this.dragging_type = undefined;
       
   521 }
       
   522 
       
   523 Tlns.Classes.Timeline.prototype.timeFromX = function(_x) {
       
   524     return Math.max(this.start_time,Math.min(this.end_time, this.start_time + _x / this.current_scale));
       
   525 }
       
   526 
       
   527 Tlns.Classes.Timeline.prototype.timeFromMouse = function(_pageX) {
       
   528     return this.timeFromX(_pageX - this.dragging_bounds.left);
       
   529 }
       
   530 
       
   531 Tlns.Classes.Timeline.prototype.universFromY = function(_y) {
       
   532     return Math.max(0,Math.min(this.univers.length, Math.floor(_y / this.univers_height)))
       
   533 }
       
   534 
       
   535 Tlns.Classes.Timeline.prototype.universFromMouse = function(_pageY) {
       
   536     return this.universFromY(_pageY - this.dragging_bounds.top);
       
   537 }
       
   538 
       
   539 Tlns.Classes.Timeline.prototype.onMouseMove = function(_event) {
       
   540     if (this.mouse_down) {
       
   541         this.is_dragging = true;
       
   542         this.hideTooltip();
       
   543         switch (this.dragging_type) {
       
   544             case "occurrence":
       
   545                 var _d = this.timeFromMouse(_event.pageX);
       
   546                 this.editing_occurrence.date = _d;
       
   547                 this.editing_occurrence.formatted_date = Tlns.Utils.dateFormat(this.editing_occurrence.date,this.tooltip_date_format);
       
   548                 var _u = this.universFromMouse(_event.pageY);
       
   549                 this.editing_occurrence.univers = this.univers[_u];
       
   550                 this.editing_occurrence.univers_id = this.univers[_u].id;
       
   551                 this.throttledDrawGrid();
       
   552             break;
       
   553             case "timeline":
       
   554                 this.setTime(this.time_at_start + Math.floor(( this.start_pos.x - _event.pageX ) / this.current_scale));
       
   555             break;
       
   556             case "link":
       
   557                 var _ctx = this.$.find('.Tl-Linking-Canvas')[0].getContext('2d');
       
   558                 _ctx.clearRect(0,0,this.main_width, this.main_height);
       
   559                 Tlns.Utils.drawArrow(
       
   560                     _ctx,
       
   561                     '#800080',
       
   562                     this.editing_occurrence.x,
       
   563                     this.editing_occurrence.y + Math.floor(this.univers_height / 2),
       
   564                     _event.pageX - this.dragging_bounds.left,
       
   565                     _event.pageY - this.dragging_bounds.top
       
   566                 );
       
   567             break;
       
   568         }
       
   569     }
       
   570 }
       
   571 
       
   572 Tlns.Classes.Timeline.prototype.onUniversLoaded = function(_data) {
       
   573     this.univers = [];
       
   574     if(_data.length) {
       
   575         this.univers_height = Math.floor(this.main_height / _data.length);
       
   576     }
       
   577     for(var _i = 0; _i < _data.length; _i++) {
       
   578         this.univers.push(new Tlns.Classes.Univers(_data[_i], this, _i));
       
   579     }
       
   580     
       
   581     this.$.find(".Ls-Univers").html(Mustache.to_html(Tlns.Templates.Univers_List, this));
       
   582     var _this = this;
       
   583     this.$.find(".Ls-Univers li.Ls-Critere").click( function() {
       
   584         $(this).toggleClass("Ls-Active");
       
   585         _this.throttledDrawList();
       
   586     });
       
   587     this.loadOccurrences();
       
   588 }
       
   589 
       
   590 Tlns.Classes.Timeline.prototype.offsetTime = function(_timeOffset) {
       
   591     this.setTime(this.central_time + _timeOffset);
       
   592 }
       
   593 
       
   594 Tlns.Classes.Timeline.prototype.setTime = function(_centralTime) {
       
   595     this.sync_now = false;
       
   596     this.central_time = _centralTime;
       
   597     this.changeSpan();
       
   598 }
       
   599 
       
   600 Tlns.Classes.Timeline.prototype.setLevel = function(_level) {
       
   601     if (_level >= 0 && _level < this.timescales.length) {
       
   602         this.$.find('.Tl-TopBar-Timescales>div').each(function() {
       
   603             var _el = $(this);
       
   604             if (_el.attr("data-level") == _level) {
       
   605                 _el.addClass("active");
       
   606             } else {
       
   607                 _el.removeClass("active");
       
   608             }
       
   609         });
       
   610         this.level = _level;
       
   611         this.changeSpan();
       
   612     }
       
   613 }
       
   614 
       
   615 Tlns.Classes.Timeline.prototype.changeSpan = function() {
       
   616     var _now = new Date().valueOf();
       
   617     if (this.sync_now) {
       
   618         this.central_time = _now;
       
   619     }
       
   620     var _timescale = this.timescales[this.level];
       
   621     this.current_scale = this.main_width / (_timescale.span);
       
   622     this.start_time = this.central_time - (_timescale.span / 2);
       
   623     this.end_time = this.central_time + (_timescale.span / 2);
       
   624     this.$.find(".Ls-From-Time").val(Tlns.Utils.dateFormat(this.start_time, '{{0hours}}:{{0minutes}}'));
       
   625     this.$.find(".Ls-From-Date").val(Tlns.Utils.dateFormat(this.start_time, '{{0dayOfMonth}}/{{0monthNumber}}/{{year}}'));
       
   626     this.$.find(".Ls-To-Time").val(Tlns.Utils.dateFormat(this.end_time, '{{0hours}}:{{0minutes}}'));
       
   627     this.$.find(".Ls-To-Date").val(Tlns.Utils.dateFormat(this.end_time, '{{0dayOfMonth}}/{{0monthNumber}}/{{year}}'));
       
   628     this.throttledDrawGrid();
       
   629     this.throttledDrawList();
       
   630 }
       
   631 
       
   632 Tlns.Classes.Timeline.prototype.drawGrid = function() {
       
   633     if (this.sync_now) {
       
   634         this.$.find('.Tl-TopBar-SyncButton').addClass("active");
       
   635     } else {
       
   636         this.$.find('.Tl-TopBar-SyncButton').removeClass("active");
       
   637     }
       
   638     var _now = new Date().valueOf(),
       
   639         _timescale = this.timescales[this.level],
       
   640         _offset = new Date().getTimezoneOffset() * 60000,
       
   641         _grid_width = Math.floor(_timescale.grid_interval * this.current_scale),
       
   642         _roundstart = Math.floor((this.start_time - _offset) / _timescale.grid_interval) * _timescale.grid_interval + _offset,
       
   643         _html = '';
       
   644     this.$.find('.Tl-TopBar-TimeSpan').html(Tlns.Utils.dateFormat(this.start_time, _timescale.start_date_format) + ' - ' + Tlns.Utils.dateFormat(this.end_time, _timescale.end_date_format));
       
   645     for (var _t = _roundstart; _t < this.end_time; _t += _timescale.grid_interval) {
       
   646         var _x = this.current_scale * (_t - this.start_time);
       
   647         if (_x > 0) {
       
   648             _html += '<div class="Tl-Grid-Column" style="width:' + _grid_width + 'px; left: ' + _x + 'px">'
       
   649             + '<div class="Tl-Grid-Label">' + Tlns.Utils.dateFormat(_t, _timescale.grid_date_format) + '</div></div>';
       
   650         }
       
   651     }
       
   652     if (this.start_time <= _now && this.end_time >= _now) {
       
   653         _html += '<div class="Tl-Grid-Now" style="left: ' + this.current_scale * (_now - this.start_time) + 'px"></div>'
       
   654     }
       
   655     this.$.find('.Tl-Grid').html(_html);
       
   656     this.drawOccurrences();
       
   657 }
       
   658 
       
   659 Tlns.Classes.Timeline.prototype.loadOccurrences = function() {
       
   660     var _this = this;
       
   661     _(this.urls_occurrences).each(function(_url_occ) {
       
   662         $.getJSON(_url_occ.url, function(_data) {
       
   663             _this.onOccurrencesLoaded(_data, _url_occ.type);
       
   664         });
       
   665     });
       
   666     
       
   667 }
       
   668 
       
   669 Tlns.Classes.Timeline.prototype.onOccurrencesLoaded = function(_data, _type) {
       
   670     for (var _i = 0; _i < _data.length; _i++) {
       
   671         this.createOrUpdateOccurrence(_type, _data[_i]);
       
   672     }
       
   673     if (!this.mouse_down) {
       
   674         this.drawOccurrences();
       
   675     }
       
   676     this.throttledDrawList();
       
   677 }
       
   678 
       
   679 Tlns.Classes.Timeline.prototype.deleteOccurrence = function(_id) {
       
   680     this.occurrences = _(this.occurrences).reject(function(_occ) {
       
   681         return _occ.id == _id;
       
   682     });
       
   683 }
       
   684 
       
   685 Tlns.Classes.Timeline.prototype.getOccurrence = function(_id) {
       
   686     return _(this.occurrences).find(function(_occ) {
       
   687         return _occ.id == _id;
       
   688     });
       
   689 }
       
   690 
       
   691 Tlns.Classes.Timeline.prototype.createOrUpdateOccurrence = function(_type, _data) {
       
   692     var _id = _type + "_" + _data.id,
       
   693         _occurrence = this.getOccurrence(_id);
       
   694     if (typeof _occurrence === "undefined") {
       
   695         _occurrence = new Tlns.Classes.Occurrence(this);
       
   696         this.occurrences.push(_occurrence);
       
   697     }
       
   698     _occurrence.update(_type, _data);
       
   699     return _occurrence;
       
   700 }
       
   701 
       
   702 Tlns.Classes.Timeline.prototype.showTooltip = function(_x, _y, _html, _isUp) {
       
   703     this.$.find('.Tl-Overlay-Box')
       
   704         .removeClass(_isUp ? 'Tl-Overlay-Down' : 'Tl-Overlay-Up')
       
   705         .addClass(_isUp ? 'Tl-Overlay-Up' : 'Tl-Overlay-Down')
       
   706         .show()
       
   707         .css({
       
   708             left: _x + "px",
       
   709             top: _y + "px"
       
   710         });
       
   711     this.$.find('.Tl-Overlay-Main').html(_html);
       
   712 }
       
   713 
       
   714 Tlns.Classes.Timeline.prototype.hideTooltip = function() {
       
   715     this.$.find('.Tl-Overlay-Box').hide();
       
   716 }
       
   717 
       
   718 Tlns.Classes.Timeline.prototype.drawOccurrences = function() {
       
   719     var _this = this,
       
   720         _visible = _(this.occurrences).filter(function(_occ) {
       
   721         return (_occ.date >= _this.start_time && _occ.date <= _this.end_time && _occ.status);
       
   722     });
       
   723     _(_visible).each(function(_occ) {
       
   724         _occ.x = _this.current_scale * (_occ.date - _this.start_time);
       
   725         _occ.y = _occ.univers.y;
       
   726         _occ.in_cluster = false;
       
   727     });
       
   728     
       
   729     var _moved = true;
       
   730     while (_moved) {
       
   731         _moved = false;
       
   732         for (var _i = 0; _i < _visible.length; _i++) {
       
   733             for (var _j = 0; _j < _i; _j++) {
       
   734                 if (_visible[_j].univers_id == _visible[_i].univers_id
       
   735                     && _visible[_j].x != _visible[_i].x
       
   736                     && Math.abs(_visible[_j].x-_visible[_i].x) < this.cluster_spacing
       
   737                 ) {
       
   738                     _moved = true;
       
   739                     _visible[_i].x = this.cluster_spacing * Math.round(_visible[_i].x / this.cluster_spacing);
       
   740                     _visible[_j].x = this.cluster_spacing * Math.round(_visible[_j].x / this.cluster_spacing);
       
   741                 }
       
   742             }
       
   743         }
       
   744     }
       
   745     var _clusters = [],
       
   746         _openCluster = false;
       
   747     for (var _i = 0; _i < _visible.length; _i++) {
       
   748         for (var _j = 0; _j < _i; _j++) {
       
   749             if (_visible[_j].univers_id == _visible[_i].univers_id && _visible[_j].x == _visible[_i].x) {
       
   750                 _visible[_j].in_cluster = true;
       
   751                 _visible[_i].in_cluster = true;
       
   752                 var _x = _visible[_j].x,
       
   753                     _y = _visible[_j].y;
       
   754                     _cluster = _(_clusters).find(function(_c) { return _c.x == _x && _c.y == _y });
       
   755                 if (typeof _cluster === "undefined") {
       
   756                     _cluster = { x: _x, y: _y, occurrences: [] };
       
   757                     _clusters.push(_cluster);
       
   758                 }
       
   759                 if ("undefined" === typeof _(_cluster.occurrences).find(function(_o) {
       
   760                     return _o.type == _visible[_j].type && _o.id == _visible[_j].id;
       
   761                 })) {
       
   762                     _cluster.occurrences.push(_visible[_j]);
       
   763                 }
       
   764                 if ("undefined" === typeof _(_cluster.occurrences).find(function(_o) {
       
   765                     return _o.type == _visible[_i].type && _o.id == _visible[_i].id;
       
   766                 })) {
       
   767                     _cluster.occurrences.push(_visible[_i]);
       
   768                 }
       
   769             }
       
   770         }
       
   771     }
       
   772     _(_clusters).each(function(_cluster) {
       
   773         _cluster.occurrences = _(_cluster.occurrences).sortBy(function(_o) {
       
   774             return _o.date;
       
   775         });
       
   776         _cluster.contents = _cluster.occurrences.map(function(_o) {
       
   777             return _o.type + ":" + _o.id;
       
   778         }).join("|");
       
   779         if (_cluster.contents == _this.open_cluster) {
       
   780             _openCluster = _cluster;
       
   781         }
       
   782     });
       
   783     
       
   784     
       
   785     var _links = [];
       
   786     
       
   787     _(_visible).each(function(_occurrence) {
       
   788         _(_occurrence.dependsOn).each(function(_dependance) {
       
   789             var _parent = _(_visible).find(function(_o) {
       
   790                 return _o.id == _dependance;
       
   791             });
       
   792             if (typeof _parent !== "undefined") {
       
   793                 _links.push({
       
   794                     from_x: _occurrence.x,
       
   795                     from_y: _occurrence.y + Math.floor(_this.univers_height / 2),
       
   796                     to_x: _parent.x,
       
   797                     to_y: _parent.y + Math.floor(_this.univers_height / 2)
       
   798                 });
       
   799             }
       
   800         });
       
   801     });
       
   802     
       
   803     var _ctx = this.$.find('.Tl-Canvas')[0].getContext('2d');
       
   804     _ctx.clearRect(0,0,this.main_width, this.main_height);
       
   805     _(_links).each(function(_link) {
       
   806         Tlns.Utils.drawArrow(_ctx, "#505050", _link.from_x,_link.from_y, _link.to_x,_link.to_y);
       
   807     });
       
   808     
       
   809     var _html = Mustache.to_html(Tlns.Templates.Occurrence, {
       
   810         occurrences:_(_visible).reject(function(_o) {return _o.in_cluster}),
       
   811         clusters: _clusters,
       
   812         open_cluster: _openCluster
       
   813     });
       
   814     this.$.find('.Tl-Occurrences').html(_html);
       
   815     
       
   816     
       
   817     if (_openCluster) {
       
   818         var _w = this.$.find('.Tl-Occurrence').width(),
       
   819             _ww = _w * _openCluster.occurrences.length;
       
   820         this.$.find('.Tl-ClusterOverlay').css({
       
   821             "margin-left": - Math.floor(_ww/2) + "px",
       
   822             width: _ww
       
   823         });
       
   824         _(_openCluster.occurrences).each(function(_o, _i) {
       
   825             _o.y = _o.y - 20;
       
   826             _o.x = _o.x - (_ww / 2) + ((_i + .5) * _w);
       
   827         });
       
   828     }
       
   829     
       
   830     this.$.find('.Tl-Occurrence').mousedown(function() {
       
   831         var _el = $(this),
       
   832             _id = _el.attr("occurrence-id");
       
   833         if (typeof _id !== "undefined") {
       
   834             _this.editing_occurrence = _this.getOccurrence(_id);
       
   835             if (typeof _this.dragging_type === "undefined" && typeof _this.editing_occurrence !== "undefined" /* && !_this.editing_occurrence.locked */ ) {
       
   836                 _this.dragging_type = "occurrence";
       
   837                 _this.editing_occurrence.editing = true;
       
   838             }
       
   839         }
       
   840     }).mouseover(function(_event) {
       
   841         var _el = $(this),
       
   842             _id = _el.attr("occurrence-id");
       
   843         if (typeof _id !== "undefined") {
       
   844             var _occurrence = _this.getOccurrence(_id);
       
   845 //            if (!_occurrence.locked) {
       
   846             _el.find('.Tl-Link').show();
       
   847 //            }
       
   848             if (!_this.is_dragging) {
       
   849                 var _html = Mustache.to_html(Tlns.Templates.OccurrenceTooltip, _occurrence);
       
   850                 _this.showTooltip(_occurrence.x, _occurrence.y, _html, (_event.pageY - _this.dragging_bounds.top) >= (.4 * _this.main_height) );
       
   851             }
       
   852         }
       
   853     }).mouseout(function() {
       
   854         var _el = $(this),
       
   855             _id = _el.attr("occurrence-id");
       
   856         if (typeof _id !== "undefined") {
       
   857             var _occurrence = _this.getOccurrence(_id);
       
   858             _this.hideTooltip();
       
   859             if (!_occurrence.editing) {
       
   860                 $(this).find('.Tl-Link').hide();
       
   861             }
       
   862         }
       
   863     }).mouseup(function() {
       
   864         var _el = $(this);
       
   865         if (_this.dragging_type == "link") {
       
   866             var _target = _el.attr("occurrence-id");
       
   867             if (_target != _this.editing_occurrence.id) {
       
   868                 _this.editing_occurrence.addDependency(_target);
       
   869                 $("body").trigger("AjoutDependanceTimeline",
       
   870                     {
       
   871                         id: _this.editing_occurrence.original_id,
       
   872                         typeOccurrence: "occurrence" + _this.editing_occurrence.type.replace(/^./,function(_l) { return _l.toUpperCase()}),
       
   873                         idCible: _target.replace(/^.*_/,''),
       
   874                         typeOccurrenceCible: "occurrence" + _target.replace(/_.*$/,'').replace(/^./,function(_l) { return _l.toUpperCase()})
       
   875                     }
       
   876                 );
       
   877             }
       
   878         }
       
   879     });
       
   880     
       
   881     this.$.find('.Tl-Link').mousedown(function() {
       
   882         var _el = $(this).parent(),
       
   883             _id = _el.attr("occurrence-id");
       
   884         _this.editing_occurrence = _this.getOccurrence(_id);
       
   885         if (typeof _this.editing_occurrence !== "undefined" /* && !_this.editing_occurrence.locked */ ) {
       
   886             _this.dragging_type = "link";
       
   887             _this.editing_occurrence.editing = true;
       
   888         }
       
   889     })
       
   890     
       
   891     this.$.find('.Tl-Cluster').click(function() {
       
   892         var _el = $(this),
       
   893             _contents = _el.attr("cluster-contents");
       
   894         if (_this.open_cluster == _contents) {
       
   895             _this.open_cluster = false;
       
   896         } else {
       
   897             _this.open_cluster = _contents;
       
   898         }
       
   899         _this.throttledDrawGrid();
       
   900     })
       
   901 }
       
   902 
       
   903 Tlns.Classes.Timeline.prototype.drawList = function() {
       
   904     var _universfilter = this.$.find(".Ls-Univers li.Ls-Active").map(function(){return $(this).attr("data")}),
       
   905         _occtypefilter = this.$.find(".Ls-Occtypes li.Ls-Active").map(function(){return $(this).attr("data")}),
       
   906         _statusfilter = this.$.find(".Ls-Occstatuses li.Ls-Active").map(function(){return $(this).attr("data")}),
       
   907         _jtfilter = this.$.find(".Ls-IsJt li.Ls-Active").map(function(){return !!+$(this).attr("data")}),
       
   908         _title = this.$.find(".Ls-Search").val() || "",
       
   909         _titleregexp = new RegExp( "(" + _title.replace(/(\W)/gm, "\\$1") + ")", "gim" ),
       
   910         _startdate = false,
       
   911         _enddate = false,
       
   912         _fromDate = this.$.find(".Ls-From-Date").val(),
       
   913         _toDate = this.$.find(".Ls-To-Date").val();
       
   914     if (_fromDate) {
       
   915         var _date = Tlns.Utils.dateFieldProcess(_fromDate),
       
   916             _time = Tlns.Utils.timeFieldProcess(this.$.find(".Ls-From-Time").val());
       
   917         _startdate = new Date(_date.year, _date.month - 1, _date.date, _time.hours, _time.minutes);
       
   918     }
       
   919     if (_toDate) {
       
   920         var _date = Tlns.Utils.dateFieldProcess(_toDate),
       
   921             _time = Tlns.Utils.timeFieldProcess(this.$.find(".Ls-To-Time").val());
       
   922         _enddate = new Date(_date.year, _date.month - 1, _date.date, _time.hours, _time.minutes);
       
   923     }
       
   924     this.$.find(".Ls-Occurrences").html(
       
   925         Mustache.to_html(
       
   926             Tlns.Templates.Occurrence_List,
       
   927             {
       
   928                 occurrences: this.occurrences.filter(function(_occ) {
       
   929                     var _titletest = (!!_occ.title.match(_titleregexp)),
       
   930                         _keep = (
       
   931                                ( !_title || _titletest )
       
   932                             && (_(_occtypefilter).indexOf(_occ.type) !== -1)
       
   933                             && (_(_universfilter).indexOf(_occ.univers_id) !== -1)
       
   934                             && (_(_statusfilter).indexOf(_occ.status) !== -1)
       
   935                             && (_(_jtfilter).indexOf(_occ.jt) !== -1)
       
   936                             && ( !_fromDate || _occ.date >= _startdate )
       
   937                             && ( !_toDate || _occ.date <= _enddate )
       
   938                         );
       
   939                     return _keep;
       
   940                 })
       
   941             }
       
   942         )
       
   943     );
       
   944     if (_title) {
       
   945         this.$.find(".Ls-Occurrence-Title").each(function() {
       
   946             $(this).html($(this).text().replace(_titleregexp, "<span style='background:yellow'>$1</span>"));
       
   947         });
       
   948     }
       
   949 }
       
   950 
       
   951 Tlns.Classes.Timeline.prototype.getUnivers = function(_id) {
       
   952     return _(this.univers).find(function(_univ) {
       
   953         return (_univ.id == _id);
       
   954     });
       
   955 }
       
   956 
       
   957 /*
       
   958  * Univers
       
   959  */
       
   960 
       
   961 Tlns.Classes.Univers = function(_data, _timeline, _index) {
       
   962     this.id = _data.idUnivers;
       
   963     this.index = _index;
       
   964     this.title = _data.nomUnivers;
       
   965 //    this.mainCharacter = _data.personnage;
       
   966     this.y = (_timeline.univers_height * _index);
       
   967 
       
   968     this.$label = $('<li>').css({
       
   969         height : _timeline.univers_height + "px"
       
   970     }).html(Mustache.to_html(Tlns.Templates.Univers, this))
       
   971       .addClass((_index % 2) ? 'Tl-Line-Odd' : 'Tl-Line-Even');
       
   972     
       
   973     _timeline.$.find('.Tl-UniversLabels').append(this.$label);
       
   974     var _txt = this.title,
       
   975         _span = this.$label.find('span');
       
   976     while (_span.outerWidth() > (_timeline.width - _timeline.main_width) && _txt) {
       
   977         _txt = _txt.substr(0, _txt.length - 1);
       
   978         _span.html(_txt + '&hellip;');
       
   979     }
       
   980 }
       
   981 
       
   982 /*
       
   983  * Occurrence
       
   984  */
       
   985 
       
   986 Tlns.Classes.Occurrence = function(_timeline) {
       
   987     this.timeline = _timeline;
       
   988 }
       
   989 
       
   990 Tlns.Classes.Occurrence.prototype.update = function(_type, _data) {
       
   991     this.type = _type;
       
   992     if (typeof _data.idOccurrencePublication !== "undefined" || typeof _data.id !== "undefined" || typeof this.original_id === "undefined") {
       
   993         this.original_id = _data.idOccurrencePublication || _data.id || Tlns.Utils.guid();
       
   994     }
       
   995     this.id = _type + "_" + this.original_id;
       
   996     if (typeof _data.date !== "undefined" || typeof _data.datePublication !== "undefined") {
       
   997         this.date = 1000 * (_data.datePublication || _data.date);
       
   998     } else {
       
   999         if (typeof this.date === "undefined") {
       
  1000             this.date = new Date().valueOf();
       
  1001         }
       
  1002     }
       
  1003     this.formatted_date = Tlns.Utils.dateFormat(this.date,Tlns.Defaults.Timeline.tooltip_date_format);
       
  1004     if (typeof _data.titre !== "undefined" || typeof this.title === "undefined") {
       
  1005         this.title = _data.titre || "<untitled>";
       
  1006     }
       
  1007     if (typeof _data.idUnivers !== "undefined") {
       
  1008         this.univers_id = _data.idUnivers;
       
  1009     }
       
  1010     this.univers = this.timeline.getUnivers(this.univers_id);
       
  1011     if (typeof _data.statut !== "undefined" || typeof this.status === "undefined") {
       
  1012         switch(_data.statut) {
       
  1013             case "Validée":
       
  1014             case "valide":
       
  1015                 this.status = "valide"
       
  1016             break;
       
  1017             case "A valider":
       
  1018             case "a_valider":
       
  1019                 this.status = "a_valider";
       
  1020             break;
       
  1021             case "A réaliser":
       
  1022             case "a_realiser":
       
  1023                 this.status = "a_realiser";
       
  1024             break;
       
  1025             default:
       
  1026                 this.status = false;
       
  1027         }
       
  1028     }
       
  1029     if (typeof _data.JT !== "undefined") {
       
  1030         this.jt = !!+_data.JT;
       
  1031     }
       
  1032     this.translated_status = Tlns.Defaults.Timeline.statuses[this.status];
       
  1033 //    this.published = (_data.publication && _data.publication == "En ligne");
       
  1034 //    this.locked = _data.verrouille || false;
       
  1035 //    this.characters = _data.personnagesSecondaires || [];
       
  1036     this.dependsOn = [];
       
  1037     if (_data.dependanceNarrative) {
       
  1038         for (var _i = 0; _i < _data.dependanceNarrative.length; _i++) {
       
  1039             this.dependsOn.push("narrative_" + _data.dependanceNarrative[_i])
       
  1040         }
       
  1041     }
       
  1042     if (_data.dependancePublication) {
       
  1043         for (var _i = 0; _i < _data.dependancePublication.length; _i++) {
       
  1044             this.dependsOn.push("publication_" + _data.dependancePublication[_i])
       
  1045         }
       
  1046     }
       
  1047     var _tmp = $('<p>').html(_data.accroche || "");
       
  1048     this.description = _tmp.text().trim().replace(/(^.{60,80})[\s].+$/m,'$1&hellip;');
       
  1049 }
       
  1050 
       
  1051 Tlns.Classes.Occurrence.prototype.addDependency = function(_id) {
       
  1052     if (_(this.dependsOn).indexOf(_id) == -1) {
       
  1053         this.dependsOn.push(_id);
       
  1054     }
       
  1055 }
       
  1056 
       
  1057 Tlns.Classes.Occurrence.prototype.removeDependency = function(_id) {
       
  1058     this.dependsOn = _(this.dependsOn).reject(function(_n) {
       
  1059         return _n == _id;
       
  1060     });
       
  1061 }
       
  1062 
       
  1063 Tlns.Classes.Occurrence.prototype.toString = function() {
       
  1064     return "Occurrence " + this.type + ': "' + this.title + '"';
       
  1065 }