src/js/model.js
changeset 983 97fef7a4b189
parent 980 9ee8c00ae5b7
child 986 f9d51dd4a3fe
equal deleted inserted replaced
982:cfcbac34d020 983:97fef7a4b189
     1 /* TODO: Separate Project-specific data from Source */
     1 /* TODO: Separate Project-specific data from Source */
     2 
     2 
     3 /* model.js is where data is stored in a standard form, whatever the serializer */
     3 /* model.js is where data is stored in a standard form, whatever the serializer */
     4 
       
     5 IriSP.Model = (function (ns) {
     4 IriSP.Model = (function (ns) {
     6     
     5     
     7     function pad(n, x, b) {
     6     function pad(n, x, b) {
     8         b = b || 10;
     7         b = b || 10;
     9         var s = (x).toString(b);
     8         var s = (x).toString(b);
    16     function rand16(n) {
    15     function rand16(n) {
    17         return pad(n, Math.floor(Math.random()*Math.pow(16,n)), 16);
    16         return pad(n, Math.floor(Math.random()*Math.pow(16,n)), 16);
    18     }
    17     }
    19     
    18     
    20     var uidbase = rand16(8) + "-" + rand16(4) + "-", uidincrement = Math.floor(Math.random()*0x10000);
    19     var uidbase = rand16(8) + "-" + rand16(4) + "-", uidincrement = Math.floor(Math.random()*0x10000);
    21 
    20     
       
    21     var charsub = [
       
    22         [ 'a', 'á', 'à', 'â', 'ä' ],
       
    23         [ 'c', 'ç' ],
       
    24         [ 'e', 'é', 'è', 'ê', 'ë' ],
       
    25         [ 'i', 'í', 'ì', 'î', 'ï' ],
       
    26         [ 'o', 'ó', 'ò', 'ô', 'ö' ]
       
    27     ];
       
    28     
       
    29     var removeChars = [
       
    30         String.fromCharCode(768), String.fromCharCode(769), String.fromCharCode(770), String.fromCharCode(771), String.fromCharCode(807),
       
    31         "{", "}", "(", ")", "[", "]", "【", "】", "、", "・", "‥", "。", "「", "」", "『", "』", "〜", ":", "!", "?", " ",
       
    32         ",", " ", ";", "(", ")", ".", "*", "+", "\\", "?", "|", "{", "}", "[", "]", "^", "#", "/"
       
    33     ]
       
    34     
    22 var Model = {
    35 var Model = {
    23     _SOURCE_STATUS_EMPTY : 0,
    36     _SOURCE_STATUS_EMPTY : 0,
    24     _SOURCE_STATUS_WAITING : 1,
    37     _SOURCE_STATUS_WAITING : 1,
    25     _SOURCE_STATUS_READY : 2,
    38     _SOURCE_STATUS_READY : 2,
    26     getUID : function() {
    39     getUID : function() {
    27         return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
    40         return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
       
    41     },
       
    42     isLocalURL : function(url) {
       
    43         var matches = url.match(/^(\w+:)\/\/([^/]+)/);
       
    44         if (matches) {
       
    45             return(matches[1] === document.location.protocol && matches[2] === document.location.host)
       
    46         }
       
    47         return true;
    28     },
    48     },
    29     regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) {
    49     regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) {
    30         var _testOnly = _testOnly || false,
    50         var _testOnly = _testOnly || false,
    31             _iexact = _iexact || false;
    51             _iexact = _iexact || false;
    32         function escapeText(_text) {
    52         function escapeText(_text) {
    43         }
    63         }
    44         if (_iexact) {
    64         if (_iexact) {
    45             _source = '^' + _source + '$';
    65             _source = '^' + _source + '$';
    46         }
    66         }
    47         return new RegExp( _source, _flags);
    67         return new RegExp( _source, _flags);
       
    68     },
       
    69     fullTextRegexps: function(_text) {
       
    70         var remsrc = "[\\" + removeChars.join("\\") + "]",
       
    71             remrx = new RegExp(remsrc,"gm"),
       
    72             txt = _text.toLowerCase().replace(remrx,"")
       
    73             res = [],
       
    74             charsrc = ns._(charsub).map(function(c) {
       
    75                 return "(" + c.join("|") + ")";
       
    76             }),
       
    77             charsrx = ns._(charsrc).map(function(c) {
       
    78                 return new RegExp(c);
       
    79             }),
       
    80             src = "";
       
    81         for (var j = 0; j < txt.length; j++) {
       
    82             if (j) {
       
    83                 src += remsrc + "*";
       
    84             }
       
    85             var l = txt[j];
       
    86             ns._(charsrc).each(function(v, k) {
       
    87                 l = l.replace(charsrx[k], v);
       
    88             });
       
    89             src += l;
       
    90         }
       
    91         return "(" + src + ")";
    48     },
    92     },
    49     isoToDate : function(_str) {
    93     isoToDate : function(_str) {
    50         // http://delete.me.uk/2005/03/iso8601.html
    94         // http://delete.me.uk/2005/03/iso8601.html
    51         var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    95         var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    52         var d = _str.match(new RegExp(regexp));
    96         var d = _str.match(new RegExp(regexp));
    69         time = (Number(date) + (offset * 60 * 1000));
   113         time = (Number(date) + (offset * 60 * 1000));
    70         var _res = new Date();
   114         var _res = new Date();
    71         _res.setTime(Number(time));
   115         _res.setTime(Number(time));
    72         return _res;
   116         return _res;
    73     },
   117     },
    74     dateToIso : function(d) {
   118     dateToIso : function(_d) {
       
   119         var d = _d ? new Date(_d) : new Date();
    75         return d.getUTCFullYear()+'-'  
   120         return d.getUTCFullYear()+'-'  
    76             + pad(2, d.getUTCMonth()+1)+'-'  
   121             + pad(2, d.getUTCMonth()+1)+'-'  
    77             + pad(2, d.getUTCDate())+'T'  
   122             + pad(2, d.getUTCDate())+'T'  
    78             + pad(2, d.getUTCHours())+':'  
   123             + pad(2, d.getUTCHours())+':'  
    79             + pad(2, d.getUTCMinutes())+':'  
   124             + pad(2, d.getUTCMinutes())+':'  
    91     this.__events = {};
   136     this.__events = {};
    92     if (typeof _directory == "undefined") {
   137     if (typeof _directory == "undefined") {
    93         console.trace();
   138         console.trace();
    94         throw "Error : new Model.List(directory): directory is undefined";
   139         throw "Error : new Model.List(directory): directory is undefined";
    95     }
   140     }
       
   141     var _this =  this;
       
   142     this.on("clear-search", function() {
       
   143         _this.searching = false;
       
   144         _this.regexp = undefined;
       
   145         _this.forEach(function(_element) {
       
   146             _element.found = undefined;
       
   147         });
       
   148         _this.trigger("search-cleared");
       
   149     })
    96 }
   150 }
    97 
   151 
    98 Model.List.prototype = new Array();
   152 Model.List.prototype = new Array();
    99 
   153 
   100 Model.List.prototype.hasId = function(_id) {
   154 Model.List.prototype.hasId = function(_id) {
   189     return this.filter(function(_element) {
   243     return this.filter(function(_element) {
   190         return _rgxp.test(_element.description) || _rgxp.test(_element.title);
   244         return _rgxp.test(_element.description) || _rgxp.test(_element.title);
   191     });
   245     });
   192 }
   246 }
   193 
   247 
       
   248 Model.List.prototype.search = function(_text) {
       
   249     if (!_text) {
       
   250         this.trigger("clear-search");
       
   251         return this;
       
   252     }
       
   253     this.searching = true;
       
   254     this.trigger("search", _text);
       
   255     var rxsource = Model.fullTextRegexps(_text)
       
   256         rgxp = new RegExp(rxsource,"im"),
       
   257         this.regexp = new RegExp(rxsource,"gim");
       
   258     var res = this.filter(function(_element, _k) {
       
   259         var titlematch = rgxp.test(_element.title),
       
   260             descmatch = rgxp.test(_element.description),
       
   261             _isfound = !!(titlematch || descmatch);
       
   262         _element.found = _isfound;
       
   263         _element.trigger(_isfound ? "found" : "not-found");
       
   264         return _isfound;
       
   265     });
       
   266     this.trigger(res.length ? "found" : "not-found",res);
       
   267     return res;
       
   268 }
       
   269 
   194 Model.List.prototype.getTitles = function() {
   270 Model.List.prototype.getTitles = function() {
   195     return this.map(function(_el) {
   271     return this.map(function(_el) {
   196         return _el.title;
   272         return _el.title;
   197     });
   273     });
   198 }
   274 }
   295     this.milliseconds = 0;
   371     this.milliseconds = 0;
   296     this.setMilliseconds(_milliseconds);
   372     this.setMilliseconds(_milliseconds);
   297 }
   373 }
   298 
   374 
   299 Model.Time.prototype.setMilliseconds = function(_milliseconds) {
   375 Model.Time.prototype.setMilliseconds = function(_milliseconds) {
   300     var _ante = _milliseconds;
   376     var _ante = this.milliseconds;
   301     switch(typeof _milliseconds) {
   377     switch(typeof _milliseconds) {
   302         case "string":
   378         case "string":
   303             this.milliseconds = parseFloat(_milliseconds);
   379             this.milliseconds = parseInt(_milliseconds);
   304             break;
   380             break;
   305         case "number":
   381         case "number":
   306             this.milliseconds = _milliseconds;
   382             this.milliseconds = Math.floor(_milliseconds);
   307             break;
   383             break;
   308         case "object":
   384         case "object":
   309             this.milliseconds = parseFloat(_milliseconds.valueOf());
   385             this.milliseconds = parseInt(_milliseconds.valueOf());
   310             break;
   386             break;
   311         default:
   387         default:
   312             this.milliseconds = 0;
   388             this.milliseconds = 0;
   313     }
   389     }
   314     if (this.milliseconds === NaN) {
   390     if (this.milliseconds === NaN) {
   327 Model.Time.prototype.getHMS = function() {
   403 Model.Time.prototype.getHMS = function() {
   328     var _totalSeconds = Math.abs(Math.floor(this.getSeconds()));
   404     var _totalSeconds = Math.abs(Math.floor(this.getSeconds()));
   329     return {
   405     return {
   330         hours : Math.floor(_totalSeconds / 3600),
   406         hours : Math.floor(_totalSeconds / 3600),
   331         minutes : (Math.floor(_totalSeconds / 60) % 60),
   407         minutes : (Math.floor(_totalSeconds / 60) % 60),
   332         seconds : _totalSeconds % 60
   408         seconds : _totalSeconds % 60,
       
   409         milliseconds: this.milliseconds % 1000
   333     } 
   410     } 
   334 }
   411 }
   335 
   412 
   336 Model.Time.prototype.add = function(_milliseconds) {
   413 Model.Time.prototype.add = function(_milliseconds) {
   337     this.milliseconds += new Model.Time(_milliseconds).milliseconds;
   414     this.milliseconds += new Model.Time(_milliseconds).milliseconds;
   339 
   416 
   340 Model.Time.prototype.valueOf = function() {
   417 Model.Time.prototype.valueOf = function() {
   341     return this.milliseconds;
   418     return this.milliseconds;
   342 }
   419 }
   343 
   420 
   344 Model.Time.prototype.toString = function() {
   421 Model.Time.prototype.toString = function(showCs) {
   345     var _hms = this.getHMS(),
   422     var _hms = this.getHMS(),
   346         _res = '';
   423         _res = '';
   347     if (_hms.hours) {
   424     if (_hms.hours) {
   348         _res += _hms.hours + ':'
   425         _res += _hms.hours + ':'
   349     }
   426     }
   350     _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds);
   427     _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds);
       
   428     if (showCs) {
       
   429         _res += "." + Math.round(_hms.milliseconds / 100)
       
   430     }
   351     return _res;
   431     return _res;
   352 }
   432 }
   353 
   433 
   354 /* Model.Reference handles references between elements
   434 /* Model.Reference handles references between elements
   355  */
   435  */
   392 
   472 
   393 /* */
   473 /* */
   394 
   474 
   395 Model.Element = function(_id, _source) {
   475 Model.Element = function(_id, _source) {
   396     this.elementType = 'element';
   476     this.elementType = 'element';
   397     if (typeof _source === "undefined") {
       
   398         return;
       
   399     }
       
   400     if (typeof _id === "undefined" || !_id) {
       
   401         _id = Model.getUID();
       
   402     }
       
   403     this.source = _source;
       
   404     this.id = _id;
       
   405     this.title = "";
   477     this.title = "";
   406     this.description = "";
   478     this.description = "";
   407     this.__events = {}
   479     this.__events = {}
   408     this.source.directory.addElement(this);
   480     if (typeof _source === "undefined") {
       
   481         return;
       
   482     }
       
   483     if (typeof _id === "undefined" || !_id) {
       
   484         _id = Model.getUID();
       
   485     }
       
   486     this.id = _id;
       
   487     this.source = _source;
       
   488     if (_source !== this) {
       
   489         this.source.directory.addElement(this);
       
   490     }
   409 }
   491 }
   410 
   492 
   411 Model.Element.prototype.toString = function() {
   493 Model.Element.prototype.toString = function() {
   412     return this.elementType + (this.elementType !== 'element' ? ', id=' + this.id + ', title="' + this.title + '"' : '');
   494     return this.elementType + (this.elementType !== 'element' ? ', id=' + this.id + ', title="' + this.title + '"' : '');
   413 }
   495 }
   472     this.on("pause", function() {
   554     this.on("pause", function() {
   473         _this.paused = true;
   555         _this.paused = true;
   474     });
   556     });
   475     this.on("timeupdate", function(_time) {
   557     this.on("timeupdate", function(_time) {
   476         _this.currentTime = _time;
   558         _this.currentTime = _time;
   477     });
       
   478 }
       
   479 
       
   480 Model.Playable.prototype = new Model.Element();
       
   481 
       
   482 Model.Playable.prototype.getCurrentTime = function() { 
       
   483     return this.currentTime;
       
   484 }
       
   485 
       
   486 Model.Playable.prototype.getVolume = function() {
       
   487     return this.volume;
       
   488 }
       
   489 
       
   490 Model.Playable.prototype.getPaused = function() {
       
   491     return this.paused;
       
   492 }
       
   493 
       
   494 Model.Playable.prototype.getMuted = function() {
       
   495     return this.muted;
       
   496 }
       
   497 
       
   498 Model.Playable.prototype.setCurrentTime = function(_time) {
       
   499     this.trigger("setcurrenttime",_time);
       
   500 }
       
   501 
       
   502 Model.Playable.prototype.setVolume = function(_vol) {
       
   503     this.trigger("setvolume",_vol);
       
   504 }
       
   505 
       
   506 Model.Playable.prototype.setMuted = function(_muted) {
       
   507     this.trigger("setmuted",_muted);
       
   508 }
       
   509 
       
   510 Model.Playable.prototype.play = function() {
       
   511     this.trigger("setplay");
       
   512 }
       
   513 
       
   514 Model.Playable.prototype.pause = function() {
       
   515     this.trigger("setpause");
       
   516 }
       
   517 
       
   518 
       
   519 /* */
       
   520 
       
   521 Model.Media = function(_id, _source) {
       
   522     Model.Playable.call(this, _id, _source);
       
   523     this.elementType = 'media';
       
   524     this.duration = new Model.Time();
       
   525     this.video = '';
       
   526     
       
   527     var _this = this;
       
   528     this.on("timeupdate", function(_time) {
       
   529         _this.getAnnotations().filter(function(_a) {
   559         _this.getAnnotations().filter(function(_a) {
   530             return (_a.end <= _time || _a.begin > _time) && _a.playing
   560             return (_a.end <= _time || _a.begin > _time) && _a.playing
   531         }).forEach(function(_a) {
   561         }).forEach(function(_a) {
   532             _a.playing = false;
   562             _a.playing = false;
   533             _a.trigger("leave");
   563             _a.trigger("leave");
       
   564             _this.trigger("leave-annotation",_a);
   534         });
   565         });
   535         _this.getAnnotations().filter(function(_a) {
   566         _this.getAnnotations().filter(function(_a) {
   536             return _a.begin <= _time && _a.end > _time && !_a.playing
   567             return _a.begin <= _time && _a.end > _time && !_a.playing
   537         }).forEach(function(_a) {
   568         }).forEach(function(_a) {
   538             _a.playing = true;
   569             _a.playing = true;
   539             _a.trigger("enter");
   570             _a.trigger("enter");
       
   571             _this.trigger("enter-annotation",_a);
   540         });
   572         });
   541     });
   573     });
       
   574 }
       
   575 
       
   576 Model.Playable.prototype = new Model.Element();
       
   577 
       
   578 Model.Playable.prototype.getCurrentTime = function() { 
       
   579     return this.currentTime;
       
   580 }
       
   581 
       
   582 Model.Playable.prototype.getVolume = function() {
       
   583     return this.volume;
       
   584 }
       
   585 
       
   586 Model.Playable.prototype.getPaused = function() {
       
   587     return this.paused;
       
   588 }
       
   589 
       
   590 Model.Playable.prototype.getMuted = function() {
       
   591     return this.muted;
       
   592 }
       
   593 
       
   594 Model.Playable.prototype.setCurrentTime = function(_time) {
       
   595     this.trigger("setcurrenttime",_time);
       
   596 }
       
   597 
       
   598 Model.Playable.prototype.setVolume = function(_vol) {
       
   599     this.trigger("setvolume",_vol);
       
   600 }
       
   601 
       
   602 Model.Playable.prototype.setMuted = function(_muted) {
       
   603     this.trigger("setmuted",_muted);
       
   604 }
       
   605 
       
   606 Model.Playable.prototype.play = function() {
       
   607     this.trigger("setplay");
       
   608 }
       
   609 
       
   610 Model.Playable.prototype.pause = function() {
       
   611     this.trigger("setpause");
       
   612 }
       
   613 
       
   614 Model.Playable.prototype.show = function() {}
       
   615 
       
   616 Model.Playable.prototype.hide = function() {}
       
   617 
       
   618 /* */
       
   619 
       
   620 Model.Media = function(_id, _source) {
       
   621     Model.Playable.call(this, _id, _source);
       
   622     this.elementType = 'media';
       
   623     this.duration = new Model.Time();
       
   624     this.video = '';
       
   625     var _this = this;
   542 }
   626 }
   543 
   627 
   544 Model.Media.prototype = new Model.Playable();
   628 Model.Media.prototype = new Model.Playable();
   545 
   629 
   546 /* Default functions to be overriden by players */
   630 /* Default functions to be overriden by players */
   608 Model.Annotation.prototype = new Model.Element();
   692 Model.Annotation.prototype = new Model.Element();
   609 
   693 
   610 Model.Annotation.prototype.setBegin = function(_beginMs) {
   694 Model.Annotation.prototype.setBegin = function(_beginMs) {
   611     this.begin.setMilliseconds(Math.max(0,_beginMs));
   695     this.begin.setMilliseconds(Math.max(0,_beginMs));
   612     this.trigger("change-begin");
   696     this.trigger("change-begin");
       
   697     if (this.end < this.begin) {
       
   698         this.setEnd(this.begin);
       
   699     }
   613 }
   700 }
   614 
   701 
   615 Model.Annotation.prototype.setEnd = function(_endMs) {
   702 Model.Annotation.prototype.setEnd = function(_endMs) {
   616     this.end.setMilliseconds(Math.min(_endMs));
   703     this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds));
   617     this.trigger("change-end");
   704     this.trigger("change-end");
       
   705     if (this.end < this.begin) {
       
   706         this.setBegin(this.end);
       
   707     }
   618 }
   708 }
   619 
   709 
   620 Model.Annotation.prototype.setDuration = function(_durMs) {
   710 Model.Annotation.prototype.setDuration = function(_durMs) {
   621     this.setEnd(_durMs + this.begin.milliseconds);
   711     this.setEnd(_durMs + this.begin.milliseconds);
   622 }
   712 }
   657 
   747 
   658 Model.MashedAnnotation = function(_mashup, _annotation) {
   748 Model.MashedAnnotation = function(_mashup, _annotation) {
   659     Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
   749     Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
   660     this.elementType = 'mashedAnnotation';
   750     this.elementType = 'mashedAnnotation';
   661     this.annotation = _annotation;
   751     this.annotation = _annotation;
   662     this.begin = new Model.Time(_mashup.duration);
   752     this.begin = new Model.Time();
   663     this.end = new Model.Time(_mashup.duration + _annotation.getDuration());
   753     this.end = new Model.Time();
       
   754     this.duration = new Model.Time();
   664     this.title = this.annotation.title;
   755     this.title = this.annotation.title;
   665     this.description = this.annotation.description;
   756     this.description = this.annotation.description;
   666     this.color = this.annotation.color;
   757     this.color = this.annotation.color;
   667     var _this = this;
   758     var _this = this;
   668     this.on("click", function() {
   759     this.on("click", function() {
   669         _mashup.setCurrentTime(_this.begin);
   760         _mashup.setCurrentTime(_this.begin);
   670     });
   761     });
       
   762     this.on("enter", function() {
       
   763         _this.annotation.trigger("enter");
       
   764     });
       
   765     this.on("leave", function() {
       
   766         _this.annotation.trigger("leave");
       
   767     });
   671 }
   768 }
   672 
   769 
   673 Model.MashedAnnotation.prototype = new Model.Element(null);
   770 Model.MashedAnnotation.prototype = new Model.Element(null);
   674 
   771 
   675 Model.MashedAnnotation.prototype.getMedia = function() {
   772 Model.MashedAnnotation.prototype.getMedia = function() {
   688     return this.annotation.getTags().getTitles();
   785     return this.annotation.getTags().getTitles();
   689 }
   786 }
   690 
   787 
   691 Model.MashedAnnotation.prototype.getDuration = function() {
   788 Model.MashedAnnotation.prototype.getDuration = function() {
   692     return this.annotation.getDuration();
   789     return this.annotation.getDuration();
       
   790 }
       
   791 
       
   792 Model.MashedAnnotation.prototype.setBegin = function(_begin) {
       
   793     this.begin.setMilliseconds(_begin);
       
   794     this.duration.setMilliseconds(this.annotation.getDuration());
       
   795     this.end.setMilliseconds(_begin + this.duration);
   693 }
   796 }
   694 
   797 
   695 /* */
   798 /* */
   696 
   799 
   697 Model.Mashup = function(_id, _source) {
   800 Model.Mashup = function(_id, _source) {
   698     Model.Playable.call(this, _id, _source);
   801     Model.Playable.call(this, _id, _source);
   699     this.elementType = 'mashup';
   802     this.elementType = 'mashup';
   700     this.duration = new Model.Time();
   803     this.duration = new Model.Time();
   701     this.segments = new Model.List(_source.directory);
   804     this.segments = new Model.List(_source.directory);
   702     this.medias = new Model.List(_source.directory);
   805     this.loaded = false;
   703     var _currentMedia = null;
       
   704     var _this = this;
   806     var _this = this;
   705     this.on("timeupdate", function(_time) {
   807     this._updateTimes = function() {
   706         _this.getAnnotations().filter(function(_a) {
   808         _this.updateTimes();
   707             return (_a.end <= _time || _a.begin > _time) && _a.playing
   809         _this.trigger("change");
   708         }).forEach(function(_a) {
   810     }
   709             _a.playing = false;
   811     this.on("add", this._updateTimes);
   710             _a.trigger("leave");
   812     this.on("remove", this._updateTimes);
   711         });
       
   712         _this.getAnnotations().filter(function(_a) {
       
   713             return _a.begin <= _time && _a.end > _time && !_a.playing
       
   714         }).forEach(function(_a) {
       
   715             _a.playing = true;
       
   716             _a.trigger("enter");
       
   717             var _m = _a.getMedia();
       
   718             if (_m !== _currentMedia) {
       
   719                 if (_currentMedia) {
       
   720                     _currentMedia.trigger("leave");
       
   721                 }
       
   722                 _m.trigger("enter");
       
   723                 _currentMedia = _m;
       
   724             }
       
   725         });
       
   726     });
       
   727 }
   813 }
   728 
   814 
   729 Model.Mashup.prototype = new Model.Playable();
   815 Model.Mashup.prototype = new Model.Playable();
   730 
   816 
   731 Model.Mashup.prototype.addSegment = function(_annotation) {
   817 Model.Mashup.prototype.checkLoaded = function() {
   732     var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation);
   818     var loaded = !!this.segments.length;
   733     this.duration.setMilliseconds(_mashedAnnotation.end);
   819     this.getMedias().forEach(function(_m) {
       
   820         loaded = loaded && _m.loaded;
       
   821     });
       
   822     this.loaded = loaded;
       
   823     if (loaded) {
       
   824         this.trigger("loadedmetadata");
       
   825     }
       
   826 }
       
   827 
       
   828 Model.Mashup.prototype.updateTimes = function() {
       
   829     var _time = 0;
       
   830     this.segments.forEach(function(_segment) {
       
   831         _segment.setBegin(_time);
       
   832         _time = _segment.end;
       
   833     });
       
   834     this.duration.setMilliseconds(_time);
       
   835 }
       
   836 
       
   837 Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) {
       
   838     var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation),
       
   839         _defer = _defer || false;
   734     this.segments.push(_mashedAnnotation);
   840     this.segments.push(_mashedAnnotation);
   735     this.medias.push(_annotation.getMedia());
   841     _annotation.on("change-begin", this._updateTimes);
   736 }
   842     _annotation.on("change-end", this._updateTimes);
   737 
   843     if (!_defer) {
   738 Model.Mashup.prototype.addSegmentById = function(_elId) {
   844         this.trigger("add");
   739     var _annotation = this.source.getElement(_elId);
   845     }
       
   846 }
       
   847 
       
   848 Model.Mashup.prototype.addAnnotationById = function(_elId, _defer) {
       
   849     var _annotation = this.source.getElement(_elId),
       
   850         _defer = _defer || false;
   740     if (typeof _annotation !== "undefined") {
   851     if (typeof _annotation !== "undefined") {
   741         this.addSegment(_annotation);
   852         this.addAnnotation(_annotation, _defer);
   742     }
   853     }
       
   854 }
       
   855 
       
   856 Model.Mashup.prototype.addAnnotations = function(_segments) {
       
   857     var _this = this;
       
   858     ns._(_segments).forEach(function(_segment) {
       
   859         _this.addAnnotation(_segment, true);
       
   860     });
       
   861     this.trigger("add");
       
   862 }
       
   863 
       
   864 Model.Mashup.prototype.addAnnotationsById = function(_segments) {
       
   865     var _this = this;
       
   866     ns._(_segments).forEach(function(_segment) {
       
   867         _this.addAnnotationById(_segment, true);
       
   868     });
       
   869     this.trigger("add");
       
   870 }
       
   871 
       
   872 Model.Mashup.prototype.removeAnnotation = function(_annotation, _defer) {
       
   873     var _defer = _defer || false;
       
   874     _annotation.off("change-begin", this._updateTimes);
       
   875     _annotation.off("change-end", this._updateTimes);
       
   876     this.segments.removeId(this.id + "_" + _annotation.id);
       
   877     if (!_defer) {
       
   878         this.trigger("remove");
       
   879     }
       
   880 }
       
   881 
       
   882 Model.Mashup.prototype.removeAnnotationById = function(_annId, _defer) {
       
   883     var _defer = _defer || false;
       
   884     var _annotation = this.source.getElement(_annId);
       
   885 
       
   886     if (_annotation) {
       
   887         this.removeAnnotation(_annotation, _defer);
       
   888     }
       
   889     if (!_defer) {
       
   890         this.trigger("remove");
       
   891     }
       
   892 }
       
   893 
       
   894 Model.Mashup.prototype.setAnnotations = function(_segments) {
       
   895     while (this.segments.length) {
       
   896         this.removeAnnotation(this.segments[0].annotation, true);
       
   897     }
       
   898     this.addAnnotations(_segments);
       
   899 }
       
   900 
       
   901 Model.Mashup.prototype.setAnnotationsById = function(_segments) {
       
   902     while (this.segments.length) {
       
   903         this.removeAnnotation(this.segments[0].annotation, true);
       
   904     }
       
   905     this.addAnnotationsById(_segments);
       
   906 }
       
   907 
       
   908 Model.Mashup.prototype.hasAnnotation = function(_annotation) {
       
   909     return !!ns._(this.segments).find(function(_s) {
       
   910         return _s.annotation === _annotation
       
   911     });
       
   912 }
       
   913 
       
   914 Model.Mashup.prototype.getAnnotation = function(_annotation) {
       
   915     return ns._(this.segments).find(function(_s) {
       
   916         return _s.annotation === _annotation
       
   917     });
       
   918 }
       
   919 
       
   920 Model.Mashup.prototype.getAnnotationById = function(_id) {
       
   921     return ns._(this.segments).find(function(_s) {
       
   922         return _s.annotation.id === _id
       
   923     });
   743 }
   924 }
   744 
   925 
   745 Model.Mashup.prototype.getAnnotations = function() {
   926 Model.Mashup.prototype.getAnnotations = function() {
   746     return this.segments;
   927     return this.segments;
   747 }
   928 }
   748 
   929 
       
   930 Model.Mashup.prototype.getOriginalAnnotations = function() {
       
   931     var annotations = new Model.List(this.source.directory);
       
   932     this.segments.forEach(function(_s) {
       
   933         annotations.push(_s.annotation);
       
   934     });
       
   935     return annotations;
       
   936 }
       
   937 
   749 Model.Mashup.prototype.getMedias = function() {
   938 Model.Mashup.prototype.getMedias = function() {
   750     return this.medias;
   939     var medias = new Model.List(this.source.directory);
       
   940     this.segments.forEach(function(_annotation) {
       
   941         medias.push(_annotation.getMedia())
       
   942     })
       
   943     return medias;
   751 }
   944 }
   752 
   945 
   753 Model.Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {
   946 Model.Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {
   754     var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
   947     var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
   755     if (_annTypes.length) {
   948     if (_annTypes.length) {
   782 }
   975 }
   783 
   976 
   784 /* */
   977 /* */
   785 
   978 
   786 Model.Source = function(_config) {
   979 Model.Source = function(_config) {
       
   980     Model.Element.call(this, false, this);
   787     this.status = Model._SOURCE_STATUS_EMPTY;
   981     this.status = Model._SOURCE_STATUS_EMPTY;
   788     this.elementType = "source";
   982     this.elementType = "source";
   789     if (typeof _config !== "undefined") {
   983     if (typeof _config !== "undefined") {
   790         var _this = this;
   984         var _this = this;
   791         ns._(_config).forEach(function(_v, _k) {
   985         ns._(_config).forEach(function(_v, _k) {
   937 
  1131 
   938 Model.RemoteSource.prototype = new Model.Source();
  1132 Model.RemoteSource.prototype = new Model.Source();
   939 
  1133 
   940 Model.RemoteSource.prototype.get = function() {
  1134 Model.RemoteSource.prototype.get = function() {
   941     this.status = Model._SOURCE_STATUS_WAITING;
  1135     this.status = Model._SOURCE_STATUS_WAITING;
   942     var _this = this;
  1136     var _this = this,
   943     this.serializer.loadData(this.url, function(_result) {
  1137         urlparams = this.url_params || {},
   944         _this.deSerialize(_result);
  1138         dataType = (Model.isLocalURL(this.url) ? "json" : "jsonp");
   945         _this.handleCallbacks();
  1139     urlparams.format = dataType;
       
  1140     ns.jQuery.ajax({
       
  1141         url: this.url,
       
  1142         dataType: dataType,
       
  1143         data: urlparams,
       
  1144         success: function(_result) {
       
  1145             _this.deSerialize(_result);
       
  1146             _this.handleCallbacks();
       
  1147         }
   946     });
  1148     });
   947 }
  1149 }
   948 
  1150 
   949 /* */
  1151 /* */
   950 
  1152 
   956 Model.Directory.prototype.remoteSource = function(_properties) {
  1158 Model.Directory.prototype.remoteSource = function(_properties) {
   957     if (typeof _properties !== "object" || typeof _properties.url === "undefined") {
  1159     if (typeof _properties !== "object" || typeof _properties.url === "undefined") {
   958         throw "Error : Model.Directory.remoteSource(configuration): configuration.url is undefined";
  1160         throw "Error : Model.Directory.remoteSource(configuration): configuration.url is undefined";
   959     }
  1161     }
   960     var _config = ns._({ directory: this }).extend(_properties);
  1162     var _config = ns._({ directory: this }).extend(_properties);
   961     if (typeof this.remoteSources[_properties.url] === "undefined") {
  1163     _config.url_params = _config.url_params || {};
   962         this.remoteSources[_properties.url] = new Model.RemoteSource(_config);
  1164     var _hash = _config.url + "?" + ns.jQuery.param(_config.url_params);
   963     }
  1165     if (typeof this.remoteSources[_hash] === "undefined") {
   964     return this.remoteSources[_properties.url];
  1166         this.remoteSources[_hash] = new Model.RemoteSource(_config);
       
  1167     }
       
  1168     return this.remoteSources[_hash];
   965 }
  1169 }
   966 
  1170 
   967 Model.Directory.prototype.newLocalSource = function(_properties) {
  1171 Model.Directory.prototype.newLocalSource = function(_properties) {
   968     var _config = ns._({ directory: this }).extend(_properties),
  1172     var _config = ns._({ directory: this }).extend(_properties),
   969         _res = new Model.Source(_config);
  1173         _res = new Model.Source(_config);