integration/js/model.js
changeset 50 9cc1b66d0880
equal deleted inserted replaced
49:cb8403125d4d 50:9cc1b66d0880
       
     1 /* TODO: Separate Project-specific data from Source */
       
     2 
       
     3 /* model.js is where data is stored in a standard form, whatever the serializer */
       
     4 IriSP.Model = (function (ns) {
       
     5     
       
     6     function pad(n, x, b) {
       
     7         b = b || 10;
       
     8         var s = (x).toString(b);
       
     9         while (s.length < n) {
       
    10             s = "0" + s;
       
    11         }
       
    12         return s;
       
    13     }
       
    14     
       
    15     function rand16(n) {
       
    16         return pad(n, Math.floor(Math.random()*Math.pow(16,n)), 16);
       
    17     }
       
    18     
       
    19     var uidbase = rand16(8) + "-" + rand16(4) + "-", uidincrement = Math.floor(Math.random()*0x10000);
       
    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     
       
    35 var Model = {
       
    36     _SOURCE_STATUS_EMPTY : 0,
       
    37     _SOURCE_STATUS_WAITING : 1,
       
    38     _SOURCE_STATUS_READY : 2,
       
    39     getUID : function() {
       
    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;
       
    48     },
       
    49     regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) {
       
    50         var _testOnly = _testOnly || false,
       
    51             _iexact = _iexact || false;
       
    52         function escapeText(_text) {
       
    53             return _text.replace(/([\\\*\+\?\|\{\[\}\]\(\)\^\$\.\#\/])/gm, '\\$1');
       
    54         }
       
    55         var _source = 
       
    56             typeof _textOrArray === "string"
       
    57             ? escapeText(_textOrArray)
       
    58             : ns._(_textOrArray).map(escapeText).join("|"),
       
    59             _flags = 'im';
       
    60         if (!_testOnly) {
       
    61             _source = '(' + _source + ')';
       
    62             _flags += 'g';
       
    63         }
       
    64         if (_iexact) {
       
    65             _source = '^' + _source + '$';
       
    66         }
       
    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 + ")";
       
    92     },
       
    93     isoToDate : function(_str) {
       
    94         // http://delete.me.uk/2005/03/iso8601.html
       
    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})))?)?)?)?";
       
    96         var d = _str.match(new RegExp(regexp));
       
    97     
       
    98         var offset = 0;
       
    99         var date = new Date(d[1], 0, 1);
       
   100     
       
   101         if (d[3]) { date.setMonth(d[3] - 1); }
       
   102         if (d[5]) { date.setDate(d[5]); }
       
   103         if (d[7]) { date.setHours(d[7]); }
       
   104         if (d[8]) { date.setMinutes(d[8]); }
       
   105         if (d[10]) { date.setSeconds(d[10]); }
       
   106         if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
       
   107         if (d[14]) {
       
   108             offset = (Number(d[16]) * 60) + Number(d[17]);
       
   109             offset *= ((d[15] == '-') ? 1 : -1);
       
   110         }
       
   111     
       
   112         offset -= date.getTimezoneOffset();
       
   113         time = (Number(date) + (offset * 60 * 1000));
       
   114         var _res = new Date();
       
   115         _res.setTime(Number(time));
       
   116         return _res;
       
   117     },
       
   118     dateToIso : function(_d) {
       
   119         var d = _d ? new Date(_d) : new Date();
       
   120         return d.getUTCFullYear()+'-'  
       
   121             + pad(2, d.getUTCMonth()+1)+'-'  
       
   122             + pad(2, d.getUTCDate())+'T'  
       
   123             + pad(2, d.getUTCHours())+':'  
       
   124             + pad(2, d.getUTCMinutes())+':'  
       
   125             + pad(2, d.getUTCSeconds())+'Z'  
       
   126     }
       
   127 }
       
   128 
       
   129 /*
       
   130  * Model.List is a class for a list of elements (e.g. annotations, medias, etc. that each have a distinct ID)
       
   131  */
       
   132 Model.List = function(_directory) {
       
   133     Array.call(this);
       
   134     this.directory = _directory;
       
   135     this.idIndex = [];
       
   136     this.__events = {};
       
   137     if (typeof _directory == "undefined") {
       
   138         console.trace();
       
   139         throw "Error : new Model.List(directory): directory is undefined";
       
   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     })
       
   150 }
       
   151 
       
   152 Model.List.prototype = new Array();
       
   153 
       
   154 Model.List.prototype.hasId = function(_id) {
       
   155     return ns._(this.idIndex).include(_id);
       
   156 }
       
   157 
       
   158 /* On recent browsers, forEach and map are defined and do what we want.
       
   159  * Otherwise, we'll use the Underscore.js functions
       
   160  */
       
   161 if (typeof Array.prototype.forEach === "undefined") {
       
   162     Model.List.prototype.forEach = function(_callback) {
       
   163         var _this = this;
       
   164         ns._(this).forEach(function(_value, _key) {
       
   165             _callback(_value, _key, _this);
       
   166         });
       
   167     }
       
   168 }
       
   169 
       
   170 if (typeof Array.prototype.map === "undefined") {
       
   171     Model.List.prototype.map = function(_callback) {
       
   172         var _this = this;
       
   173         return ns._(this).map(function(_value, _key) {
       
   174             return _callback(_value, _key, _this);
       
   175         });
       
   176     }
       
   177 }
       
   178 
       
   179 Model.List.prototype.pluck = function(_key) {
       
   180     return this.map(function(_value) {
       
   181         return _value[_key];
       
   182     });
       
   183 }
       
   184 
       
   185 /* We override Array's filter function because it doesn't return an Model.List
       
   186  */
       
   187 Model.List.prototype.filter = function(_callback) {
       
   188     var _this = this,
       
   189         _res = new Model.List(this.directory);
       
   190     _res.addElements(ns._(this).filter(function(_value, _key) {
       
   191         return _callback(_value, _key, _this);
       
   192     }));
       
   193     return _res;
       
   194 }
       
   195 
       
   196 Model.List.prototype.slice = function(_start, _end) {
       
   197     var _res = new Model.List(this.directory);
       
   198     _res.addElements(Array.prototype.slice.call(this, _start, _end));
       
   199     return _res;
       
   200 }
       
   201 
       
   202 Model.List.prototype.splice = function(_start, _end) {
       
   203     var _res = new Model.List(this.directory);
       
   204     _res.addElements(Array.prototype.splice.call(this, _start, _end));
       
   205     this.idIndex.splice(_start, _end);
       
   206     return _res;
       
   207 }
       
   208 
       
   209 /* Array has a sort function, but it's not as interesting as Underscore.js's sortBy
       
   210  * and won't return a new Model.List
       
   211  */
       
   212 Model.List.prototype.sortBy = function(_callback) {
       
   213     var _this = this,
       
   214         _res = new Model.List(this.directory);
       
   215     _res.addElements(ns._(this).sortBy(function(_value, _key) {
       
   216         return _callback(_value, _key, _this);
       
   217     }));
       
   218     return _res;
       
   219 }
       
   220 
       
   221 /* Title and Description are basic information for (almost) all element types,
       
   222  * here we can search by these criteria
       
   223  */
       
   224 Model.List.prototype.searchByTitle = function(_text, _iexact) {
       
   225     var _iexact = _iexact || false,
       
   226         _rgxp = Model.regexpFromTextOrArray(_text, true);
       
   227     return this.filter(function(_element) {
       
   228         return _rgxp.test(_element.title);
       
   229     });
       
   230 }
       
   231 
       
   232 Model.List.prototype.searchByDescription = function(_text, _iexact) {
       
   233     var _iexact = _iexact || false,
       
   234         _rgxp = Model.regexpFromTextOrArray(_text, true);
       
   235     return this.filter(function(_element) {
       
   236         return _rgxp.test(_element.description);
       
   237     });
       
   238 }
       
   239 
       
   240 Model.List.prototype.searchByTextFields = function(_text, _iexact) {
       
   241     var _iexact = _iexact || false,
       
   242         _rgxp =  Model.regexpFromTextOrArray(_text, true);
       
   243     return this.filter(function(_element) {
       
   244         var keywords = (_element.keywords || _element.getTagTexts() || []).join(", ");
       
   245         return _rgxp.test(_element.description) || _rgxp.test(_element.title) || _rgxp.test(keywords);
       
   246     });
       
   247 }
       
   248 
       
   249 Model.List.prototype.search = function(_text) {
       
   250     if (!_text) {
       
   251         this.trigger("clear-search");
       
   252         return this;
       
   253     }
       
   254     this.searching = true;
       
   255     this.trigger("search", _text);
       
   256     var rxsource = Model.fullTextRegexps(_text)
       
   257         rgxp = new RegExp(rxsource,"im"),
       
   258         this.regexp = new RegExp(rxsource,"gim");
       
   259     var res = this.filter(function(_element, _k) {
       
   260         var titlematch = rgxp.test(_element.title),
       
   261             descmatch = rgxp.test(_element.description),
       
   262             _isfound = !!(titlematch || descmatch);
       
   263         _element.found = _isfound;
       
   264         _element.trigger(_isfound ? "found" : "not-found");
       
   265         return _isfound;
       
   266     });
       
   267     this.trigger(res.length ? "found" : "not-found",res);
       
   268     return res;
       
   269 }
       
   270 
       
   271 Model.List.prototype.getTitles = function() {
       
   272     return this.map(function(_el) {
       
   273         return _el.title;
       
   274     });
       
   275 }
       
   276 
       
   277 Model.List.prototype.addId = function(_id) {
       
   278     var _el = this.directory.getElement(_id)
       
   279     if (!this.hasId(_id) && typeof _el !== "undefined") {
       
   280         this.idIndex.push(_id);
       
   281         Array.prototype.push.call(this, _el);
       
   282     }
       
   283 }
       
   284 
       
   285 Model.List.prototype.push = function(_el) {
       
   286     if (typeof _el === "undefined") {
       
   287         return;
       
   288     }
       
   289     var _index = (ns._(this.idIndex).indexOf(_el.id));
       
   290     if (_index === -1) {
       
   291         this.idIndex.push(_el.id);
       
   292         Array.prototype.push.call(this, _el);
       
   293     } else {
       
   294         this[_index] = _el;
       
   295     }
       
   296 }
       
   297 
       
   298 Model.List.prototype.addIds = function(_array) {
       
   299     var _l = _array.length,
       
   300         _this = this;
       
   301     ns._(_array).forEach(function(_id) {
       
   302         _this.addId(_id);
       
   303     });
       
   304 }
       
   305 
       
   306 Model.List.prototype.addElements = function(_array) {
       
   307     var _this = this;
       
   308     ns._(_array).forEach(function(_el) {
       
   309         _this.push(_el);
       
   310     });
       
   311 }
       
   312 
       
   313 Model.List.prototype.removeId = function(_id, _deleteFromDirectory) {
       
   314     var _deleteFromDirectory = _deleteFromDirectory || false,
       
   315         _index = (ns._(this.idIndex).indexOf(_id));
       
   316     if (_index !== -1) {
       
   317         this.splice(_index,1);
       
   318     }
       
   319     if (_deleteFromDirectory) {
       
   320         delete this.directory.elements[_id];
       
   321     }
       
   322 }
       
   323 
       
   324 Model.List.prototype.removeElement = function(_el, _deleteFromDirectory) {
       
   325     var _deleteFromDirectory = _deleteFromDirectory || false;
       
   326     this.removeId(_el.id);
       
   327 }
       
   328 
       
   329 Model.List.prototype.removeIds = function(_list, _deleteFromDirectory) {
       
   330     var _deleteFromDirectory = _deleteFromDirectory || false,
       
   331         _this = this;
       
   332     ns._(_list).forEach(function(_id) {
       
   333         _this.removeId(_id);
       
   334     });
       
   335 }
       
   336 
       
   337 Model.List.prototype.removeElements = function(_list, _deleteFromDirectory) {
       
   338     var _deleteFromDirectory = _deleteFromDirectory || false,
       
   339         _this = this;
       
   340     ns._(_list).forEach(function(_el) {
       
   341         _this.removeElement(_el);
       
   342     });
       
   343 }
       
   344 
       
   345 Model.List.prototype.on = function(_event, _callback) {
       
   346     if (typeof this.__events[_event] === "undefined") {
       
   347         this.__events[_event] = [];
       
   348     }
       
   349     this.__events[_event].push(_callback);
       
   350 }
       
   351 
       
   352 Model.List.prototype.off = function(_event, _callback) {
       
   353     if (typeof this.__events[_event] !== "undefined") {
       
   354         this.__events[_event] = ns._(this.__events[_event]).reject(function(_fn) {
       
   355             return _fn === _callback;
       
   356         });
       
   357     }
       
   358 }
       
   359 
       
   360 Model.List.prototype.trigger = function(_event, _data) {
       
   361     var _list = this;
       
   362     ns._(this.__events[_event]).each(function(_callback) {
       
   363         _callback.call(_list, _data);
       
   364     });
       
   365 }
       
   366 
       
   367 /* A simple time management object, that helps converting millisecs to seconds and strings,
       
   368  * without the clumsiness of the original Date object.
       
   369  */
       
   370 
       
   371 Model.Time = function(_milliseconds) {
       
   372     this.milliseconds = 0;
       
   373     this.setMilliseconds(_milliseconds);
       
   374 }
       
   375 
       
   376 Model.Time.prototype.setMilliseconds = function(_milliseconds) {
       
   377     var _ante = this.milliseconds;
       
   378     switch(typeof _milliseconds) {
       
   379         case "string":
       
   380             this.milliseconds = parseInt(_milliseconds);
       
   381             break;
       
   382         case "number":
       
   383             this.milliseconds = Math.floor(_milliseconds);
       
   384             break;
       
   385         case "object":
       
   386             this.milliseconds = parseInt(_milliseconds.valueOf());
       
   387             break;
       
   388         default:
       
   389             this.milliseconds = 0;
       
   390     }
       
   391     if (this.milliseconds === NaN) {
       
   392         this.milliseconds = _ante;
       
   393     }
       
   394 }
       
   395 
       
   396 Model.Time.prototype.setSeconds = function(_seconds) {
       
   397     this.milliseconds = 1000 * _seconds;
       
   398 }
       
   399 
       
   400 Model.Time.prototype.getSeconds = function() {
       
   401     return this.milliseconds / 1000;
       
   402 }
       
   403 
       
   404 Model.Time.prototype.getHMS = function() {
       
   405     var _totalSeconds = Math.abs(Math.floor(this.getSeconds()));
       
   406     return {
       
   407         hours : Math.floor(_totalSeconds / 3600),
       
   408         minutes : (Math.floor(_totalSeconds / 60) % 60),
       
   409         seconds : _totalSeconds % 60,
       
   410         milliseconds: this.milliseconds % 1000
       
   411     } 
       
   412 }
       
   413 
       
   414 Model.Time.prototype.add = function(_milliseconds) {
       
   415     this.milliseconds += new Model.Time(_milliseconds).milliseconds;
       
   416 }
       
   417 
       
   418 Model.Time.prototype.valueOf = function() {
       
   419     return this.milliseconds;
       
   420 }
       
   421 
       
   422 Model.Time.prototype.toString = function(showCs) {
       
   423     var _hms = this.getHMS(),
       
   424         _res = '';
       
   425     if (_hms.hours) {
       
   426         _res += _hms.hours + ':'
       
   427     }
       
   428     _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds);
       
   429     if (showCs) {
       
   430         _res += "." + Math.floor(_hms.milliseconds / 100)
       
   431     }
       
   432     return _res;
       
   433 }
       
   434 
       
   435 /* Model.Reference handles references between elements
       
   436  */
       
   437 
       
   438 Model.Reference = function(_source, _idRef) {
       
   439     this.source = _source;
       
   440     this.id = _idRef;
       
   441     if (typeof _idRef === "object") {
       
   442         this.isList = true;
       
   443     } else {
       
   444         this.isList = false;
       
   445     }
       
   446     this.refresh();
       
   447 }
       
   448 
       
   449 Model.Reference.prototype.refresh = function() {
       
   450     if (this.isList) {
       
   451         this.contents = new Model.List(this.source.directory);
       
   452         this.contents.addIds(this.id);
       
   453     } else {
       
   454         this.contents = this.source.getElement(this.id);
       
   455     }
       
   456     
       
   457 }
       
   458 
       
   459 Model.Reference.prototype.getContents = function() {
       
   460     if (typeof this.contents === "undefined" || (this.isList && this.contents.length != this.id.length)) {
       
   461         this.refresh();
       
   462     }
       
   463     return this.contents;
       
   464 }
       
   465 
       
   466 Model.Reference.prototype.isOrHasId = function(_idRef) {
       
   467     if (this.isList) {
       
   468         return (ns._(this.id).indexOf(_idRef) !== -1)
       
   469     } else {
       
   470         return (this.id == _idRef);
       
   471     }
       
   472 }
       
   473 
       
   474 /* */
       
   475 
       
   476 Model.Element = function(_id, _source) {
       
   477     this.elementType = 'element';
       
   478     this.title = "";
       
   479     this.description = "";
       
   480     this.__events = {}
       
   481     if (typeof _source === "undefined") {
       
   482         return;
       
   483     }
       
   484     if (typeof _id === "undefined" || !_id) {
       
   485         _id = Model.getUID();
       
   486     }
       
   487     this.id = _id;
       
   488     this.source = _source;
       
   489     if (_source !== this) {
       
   490         this.source.directory.addElement(this);
       
   491     }
       
   492 }
       
   493 
       
   494 Model.Element.prototype.toString = function() {
       
   495     return this.elementType + (this.elementType !== 'element' ? ', id=' + this.id + ', title="' + this.title + '"' : '');
       
   496 }
       
   497 
       
   498 Model.Element.prototype.setReference = function(_elementType, _idRef) {
       
   499     this[_elementType] = new Model.Reference(this.source, _idRef);
       
   500 }
       
   501 
       
   502 Model.Element.prototype.getReference = function(_elementType) {
       
   503     if (typeof this[_elementType] !== "undefined") {
       
   504         return this[_elementType].getContents();
       
   505     }
       
   506 }
       
   507 
       
   508 Model.Element.prototype.getRelated = function(_elementType, _global) {
       
   509     _global = (typeof _global !== "undefined" && _global);
       
   510     var _this = this;
       
   511     return this.source.getList(_elementType, _global).filter(function(_el) {
       
   512         var _ref = _el[_this.elementType];
       
   513         return _ref && _ref.isOrHasId(_this.id);
       
   514     });
       
   515 }
       
   516 
       
   517 Model.Element.prototype.on = function(_event, _callback) {
       
   518     if (typeof this.__events[_event] === "undefined") {
       
   519         this.__events[_event] = [];
       
   520     }
       
   521     this.__events[_event].push(_callback);
       
   522 }
       
   523 
       
   524 Model.Element.prototype.off = function(_event, _callback) {
       
   525     if (typeof this.__events[_event] !== "undefined") {
       
   526         this.__events[_event] = ns._(this.__events[_event]).reject(function(_fn) {
       
   527             return _fn === _callback;
       
   528         });
       
   529     }
       
   530 }
       
   531 
       
   532 Model.Element.prototype.trigger = function(_event, _data) {
       
   533     var _element = this;
       
   534     ns._(this.__events[_event]).each(function(_callback) {
       
   535         _callback.call(_element, _data);
       
   536     });
       
   537 }
       
   538 
       
   539 /* */
       
   540 
       
   541 Model.Playable = function(_id, _source) {
       
   542     Model.Element.call(this, _id, _source);
       
   543     if (typeof _source === "undefined") {
       
   544         return;
       
   545     }
       
   546     this.elementType = 'playable';
       
   547     this.currentTime = new Model.Time();
       
   548     this.volume = .5;
       
   549     this.paused = true;
       
   550     this.muted = false;
       
   551     var _this = this;
       
   552     this.on("play", function() {
       
   553         _this.paused = false;
       
   554     });
       
   555     this.on("pause", function() {
       
   556         _this.paused = true;
       
   557     });
       
   558     this.on("timeupdate", function(_time) {
       
   559         _this.currentTime = _time;
       
   560         _this.getAnnotations().filter(function(_a) {
       
   561             return (_a.end <= _time || _a.begin > _time) && _a.playing
       
   562         }).forEach(function(_a) {
       
   563             _a.playing = false;
       
   564             _a.trigger("leave");
       
   565             _this.trigger("leave-annotation",_a);
       
   566         });
       
   567         _this.getAnnotations().filter(function(_a) {
       
   568             return _a.begin <= _time && _a.end > _time && !_a.playing
       
   569         }).forEach(function(_a) {
       
   570             _a.playing = true;
       
   571             _a.trigger("enter");
       
   572             _this.trigger("enter-annotation",_a);
       
   573         });
       
   574     });
       
   575 }
       
   576 
       
   577 Model.Playable.prototype = new Model.Element();
       
   578 
       
   579 Model.Playable.prototype.getCurrentTime = function() { 
       
   580     return this.currentTime;
       
   581 }
       
   582 
       
   583 Model.Playable.prototype.getVolume = function() {
       
   584     return this.volume;
       
   585 }
       
   586 
       
   587 Model.Playable.prototype.getPaused = function() {
       
   588     return this.paused;
       
   589 }
       
   590 
       
   591 Model.Playable.prototype.getMuted = function() {
       
   592     return this.muted;
       
   593 }
       
   594 
       
   595 Model.Playable.prototype.setCurrentTime = function(_time) {
       
   596     this.trigger("setcurrenttime",_time);
       
   597 }
       
   598 
       
   599 Model.Playable.prototype.setVolume = function(_vol) {
       
   600     this.trigger("setvolume",_vol);
       
   601 }
       
   602 
       
   603 Model.Playable.prototype.setMuted = function(_muted) {
       
   604     this.trigger("setmuted",_muted);
       
   605 }
       
   606 
       
   607 Model.Playable.prototype.play = function() {
       
   608     this.trigger("setplay");
       
   609 }
       
   610 
       
   611 Model.Playable.prototype.pause = function() {
       
   612     this.trigger("setpause");
       
   613 }
       
   614 
       
   615 Model.Playable.prototype.show = function() {}
       
   616 
       
   617 Model.Playable.prototype.hide = function() {}
       
   618 
       
   619 /* */
       
   620 
       
   621 Model.Media = function(_id, _source) {
       
   622     Model.Playable.call(this, _id, _source);
       
   623     this.elementType = 'media';
       
   624     this.duration = new Model.Time();
       
   625     this.video = '';
       
   626     var _this = this;
       
   627 }
       
   628 
       
   629 Model.Media.prototype = new Model.Playable();
       
   630 
       
   631 /* Default functions to be overriden by players */
       
   632     
       
   633 Model.Media.prototype.setDuration = function(_durationMs) {
       
   634     this.duration.setMilliseconds(_durationMs);
       
   635 }
       
   636 
       
   637 Model.Media.prototype.getAnnotations = function() {
       
   638     return this.getRelated("annotation");
       
   639 }
       
   640 
       
   641 Model.Media.prototype.getAnnotationsByTypeTitle = function(_title) {
       
   642     var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
       
   643     if (_annTypes.length) {
       
   644         return this.getAnnotations().filter(function(_annotation) {
       
   645             return ns._(_annTypes).indexOf(_annotation.getAnnotationType().id) !== -1;
       
   646         });
       
   647     } else {
       
   648         return new Model.List(this.source.directory)
       
   649     }
       
   650 }
       
   651 
       
   652 /* */
       
   653 
       
   654 Model.Tag = function(_id, _source) {
       
   655     Model.Element.call(this, _id, _source);
       
   656     this.elementType = 'tag';
       
   657 }
       
   658 
       
   659 Model.Tag.prototype = new Model.Element();
       
   660 
       
   661 Model.Tag.prototype.getAnnotations = function() {
       
   662     return this.getRelated("annotation");
       
   663 }
       
   664 
       
   665 /* */
       
   666 Model.AnnotationType = function(_id, _source) {
       
   667     Model.Element.call(this, _id, _source);
       
   668     this.elementType = 'annotationType';
       
   669 }
       
   670 
       
   671 Model.AnnotationType.prototype = new Model.Element();
       
   672 
       
   673 Model.AnnotationType.prototype.getAnnotations = function() {
       
   674     return this.getRelated("annotation");
       
   675 }
       
   676 
       
   677 /* Annotation
       
   678  * */
       
   679 
       
   680 Model.Annotation = function(_id, _source) {
       
   681     Model.Element.call(this, _id, _source);
       
   682     this.elementType = 'annotation';
       
   683     this.begin = new Model.Time();
       
   684     this.end = new Model.Time();
       
   685     this.tag = new Model.Reference(_source, []);
       
   686     this.playing = false;
       
   687     var _this = this;
       
   688     this.on("click", function() {
       
   689         _this.getMedia().setCurrentTime(_this.begin);
       
   690     });
       
   691 }
       
   692 
       
   693 Model.Annotation.prototype = new Model.Element();
       
   694 
       
   695 Model.Annotation.prototype.setBegin = function(_beginMs) {
       
   696     this.begin.setMilliseconds(Math.max(0,_beginMs));
       
   697     this.trigger("change-begin");
       
   698     if (this.end < this.begin) {
       
   699         this.setEnd(this.begin);
       
   700     }
       
   701 }
       
   702 
       
   703 Model.Annotation.prototype.setEnd = function(_endMs) {
       
   704     this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds));
       
   705     this.trigger("change-end");
       
   706     if (this.end < this.begin) {
       
   707         this.setBegin(this.end);
       
   708     }
       
   709 }
       
   710 
       
   711 Model.Annotation.prototype.setDuration = function(_durMs) {
       
   712     this.setEnd(_durMs + this.begin.milliseconds);
       
   713 }
       
   714 
       
   715 Model.Annotation.prototype.setMedia = function(_idRef) {
       
   716     this.setReference("media", _idRef);
       
   717 }
       
   718 
       
   719 Model.Annotation.prototype.getMedia = function() {
       
   720     return this.getReference("media");
       
   721 }
       
   722 
       
   723 Model.Annotation.prototype.setAnnotationType = function(_idRef) {
       
   724     this.setReference("annotationType", _idRef);
       
   725 }
       
   726 
       
   727 Model.Annotation.prototype.getAnnotationType = function() {
       
   728     return this.getReference("annotationType");
       
   729 }
       
   730 
       
   731 Model.Annotation.prototype.setTags = function(_idRefs) {
       
   732     this.setReference("tag", _idRefs);
       
   733 }
       
   734 
       
   735 Model.Annotation.prototype.getTags = function() {
       
   736     return this.getReference("tag");
       
   737 }
       
   738 
       
   739 Model.Annotation.prototype.getTagTexts = function() {
       
   740     return this.getTags().getTitles();
       
   741 }
       
   742 
       
   743 Model.Annotation.prototype.getDuration = function() {
       
   744     return new Model.Time(this.end.milliseconds - this.begin.milliseconds)
       
   745 }
       
   746 
       
   747 /* */
       
   748 
       
   749 Model.MashedAnnotation = function(_mashup, _annotation) {
       
   750     Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
       
   751     this.elementType = 'mashedAnnotation';
       
   752     this.annotation = _annotation;
       
   753     this.begin = new Model.Time();
       
   754     this.end = new Model.Time();
       
   755     this.duration = new Model.Time();
       
   756     this.title = this.annotation.title;
       
   757     this.description = this.annotation.description;
       
   758     this.color = this.annotation.color;
       
   759     var _this = this;
       
   760     this.on("click", function() {
       
   761         _mashup.setCurrentTime(_this.begin);
       
   762     });
       
   763     this.on("enter", function() {
       
   764         _this.annotation.trigger("enter");
       
   765     });
       
   766     this.on("leave", function() {
       
   767         _this.annotation.trigger("leave");
       
   768     });
       
   769 }
       
   770 
       
   771 Model.MashedAnnotation.prototype = new Model.Element(null);
       
   772 
       
   773 Model.MashedAnnotation.prototype.getMedia = function() {
       
   774     return this.annotation.getReference("media");
       
   775 }
       
   776 
       
   777 Model.MashedAnnotation.prototype.getAnnotationType = function() {
       
   778     return this.annotation.getReference("annotationType");
       
   779 }
       
   780 
       
   781 Model.MashedAnnotation.prototype.getTags = function() {
       
   782     return this.annotation.getReference("tag");
       
   783 }
       
   784 
       
   785 Model.MashedAnnotation.prototype.getTagTexts = function() {
       
   786     return this.annotation.getTags().getTitles();
       
   787 }
       
   788 
       
   789 Model.MashedAnnotation.prototype.getDuration = function() {
       
   790     return this.annotation.getDuration();
       
   791 }
       
   792 
       
   793 Model.MashedAnnotation.prototype.setBegin = function(_begin) {
       
   794     this.begin.setMilliseconds(_begin);
       
   795     this.duration.setMilliseconds(this.annotation.getDuration());
       
   796     this.end.setMilliseconds(_begin + this.duration);
       
   797 }
       
   798 
       
   799 /* */
       
   800 
       
   801 Model.Mashup = function(_id, _source) {
       
   802     Model.Playable.call(this, _id, _source);
       
   803     this.elementType = 'mashup';
       
   804     this.duration = new Model.Time();
       
   805     this.segments = new Model.List(_source.directory);
       
   806     this.loaded = false;
       
   807     var _this = this;
       
   808     this._updateTimes = function() {
       
   809         _this.updateTimes();
       
   810         _this.trigger("change");
       
   811     }
       
   812     this.on("add", this._updateTimes);
       
   813     this.on("remove", this._updateTimes);
       
   814 }
       
   815 
       
   816 Model.Mashup.prototype = new Model.Playable();
       
   817 
       
   818 Model.Mashup.prototype.checkLoaded = function() {
       
   819     var loaded = !!this.segments.length;
       
   820     this.getMedias().forEach(function(_m) {
       
   821         loaded = loaded && _m.loaded;
       
   822     });
       
   823     this.loaded = loaded;
       
   824     if (loaded) {
       
   825         this.trigger("loadedmetadata");
       
   826     }
       
   827 }
       
   828 
       
   829 Model.Mashup.prototype.updateTimes = function() {
       
   830     var _time = 0;
       
   831     this.segments.forEach(function(_segment) {
       
   832         _segment.setBegin(_time);
       
   833         _time = _segment.end;
       
   834     });
       
   835     this.duration.setMilliseconds(_time);
       
   836 }
       
   837 
       
   838 Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) {
       
   839     var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation),
       
   840         _defer = _defer || false;
       
   841     this.segments.push(_mashedAnnotation);
       
   842     _annotation.on("change-begin", this._updateTimes);
       
   843     _annotation.on("change-end", this._updateTimes);
       
   844     if (!_defer) {
       
   845         this.trigger("add");
       
   846     }
       
   847 }
       
   848 
       
   849 Model.Mashup.prototype.addAnnotationById = function(_elId, _defer) {
       
   850     var _annotation = this.source.getElement(_elId),
       
   851         _defer = _defer || false;
       
   852     if (typeof _annotation !== "undefined") {
       
   853         this.addAnnotation(_annotation, _defer);
       
   854     }
       
   855 }
       
   856 
       
   857 Model.Mashup.prototype.addAnnotations = function(_segments) {
       
   858     var _this = this;
       
   859     ns._(_segments).forEach(function(_segment) {
       
   860         _this.addAnnotation(_segment, true);
       
   861     });
       
   862     this.trigger("add");
       
   863 }
       
   864 
       
   865 Model.Mashup.prototype.addAnnotationsById = function(_segments) {
       
   866     var _this = this;
       
   867     ns._(_segments).forEach(function(_segment) {
       
   868         _this.addAnnotationById(_segment, true);
       
   869     });
       
   870     this.trigger("add");
       
   871 }
       
   872 
       
   873 Model.Mashup.prototype.removeAnnotation = function(_annotation, _defer) {
       
   874     var _defer = _defer || false;
       
   875     _annotation.off("change-begin", this._updateTimes);
       
   876     _annotation.off("change-end", this._updateTimes);
       
   877     this.segments.removeId(this.id + "_" + _annotation.id);
       
   878     if (!_defer) {
       
   879         this.trigger("remove");
       
   880     }
       
   881 }
       
   882 
       
   883 Model.Mashup.prototype.removeAnnotationById = function(_annId, _defer) {
       
   884     var _defer = _defer || false;
       
   885     var _annotation = this.source.getElement(_annId);
       
   886 
       
   887     if (_annotation) {
       
   888         this.removeAnnotation(_annotation, _defer);
       
   889     }
       
   890     if (!_defer) {
       
   891         this.trigger("remove");
       
   892     }
       
   893 }
       
   894 
       
   895 Model.Mashup.prototype.setAnnotations = function(_segments) {
       
   896     while (this.segments.length) {
       
   897         this.removeAnnotation(this.segments[0].annotation, true);
       
   898     }
       
   899     this.addAnnotations(_segments);
       
   900 }
       
   901 
       
   902 Model.Mashup.prototype.setAnnotationsById = function(_segments) {
       
   903     while (this.segments.length) {
       
   904         this.removeAnnotation(this.segments[0].annotation, true);
       
   905     }
       
   906     this.addAnnotationsById(_segments);
       
   907 }
       
   908 
       
   909 Model.Mashup.prototype.hasAnnotation = function(_annotation) {
       
   910     return !!ns._(this.segments).find(function(_s) {
       
   911         return _s.annotation === _annotation
       
   912     });
       
   913 }
       
   914 
       
   915 Model.Mashup.prototype.getAnnotation = function(_annotation) {
       
   916     return ns._(this.segments).find(function(_s) {
       
   917         return _s.annotation === _annotation
       
   918     });
       
   919 }
       
   920 
       
   921 Model.Mashup.prototype.getAnnotationById = function(_id) {
       
   922     return ns._(this.segments).find(function(_s) {
       
   923         return _s.annotation.id === _id
       
   924     });
       
   925 }
       
   926 
       
   927 Model.Mashup.prototype.getAnnotations = function() {
       
   928     return this.segments;
       
   929 }
       
   930 
       
   931 Model.Mashup.prototype.getOriginalAnnotations = function() {
       
   932     var annotations = new Model.List(this.source.directory);
       
   933     this.segments.forEach(function(_s) {
       
   934         annotations.push(_s.annotation);
       
   935     });
       
   936     return annotations;
       
   937 }
       
   938 
       
   939 Model.Mashup.prototype.getMedias = function() {
       
   940     var medias = new Model.List(this.source.directory);
       
   941     this.segments.forEach(function(_annotation) {
       
   942         medias.push(_annotation.getMedia())
       
   943     })
       
   944     return medias;
       
   945 }
       
   946 
       
   947 Model.Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {
       
   948     var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
       
   949     if (_annTypes.length) {
       
   950         return this.getAnnotations().filter(function(_annotation) {
       
   951             return ns._(_annTypes).indexOf(_annotation.getAnnotationType().id) !== -1;
       
   952         });
       
   953     } else {
       
   954         return new Model.List(this.source.directory)
       
   955     }
       
   956 }
       
   957 
       
   958 Model.Mashup.prototype.getAnnotationAtTime = function(_time) {
       
   959     var _list = this.segments.filter(function(_annotation) {
       
   960         return _annotation.begin <= _time && _annotation.end > _time;
       
   961     });
       
   962     if (_list.length) {
       
   963         return _list[0];
       
   964     } else {
       
   965         return undefined;
       
   966     }
       
   967 }
       
   968 
       
   969 Model.Mashup.prototype.getMediaAtTime = function(_time) {
       
   970     var _annotation = this.getAnnotationAtTime(_time);
       
   971     if (typeof _annotation !== "undefined") {
       
   972         return _annotation.getMedia();
       
   973     } else {
       
   974         return undefined;
       
   975     }
       
   976 }
       
   977 
       
   978 /* */
       
   979 
       
   980 Model.Source = function(_config) {
       
   981     Model.Element.call(this, false, this);
       
   982     this.status = Model._SOURCE_STATUS_EMPTY;
       
   983     this.elementType = "source";
       
   984     if (typeof _config !== "undefined") {
       
   985         var _this = this;
       
   986         ns._(_config).forEach(function(_v, _k) {
       
   987             _this[_k] = _v;
       
   988         })
       
   989         this.callbackQueue = [];
       
   990         this.contents = {};
       
   991         this.get();
       
   992     }
       
   993 }
       
   994 
       
   995 Model.Source.prototype = new Model.Element();
       
   996 
       
   997 Model.Source.prototype.addList = function(_listId, _contents) {
       
   998     if (typeof this.contents[_listId] === "undefined") {
       
   999         this.contents[_listId] = new Model.List(this.directory);
       
  1000     }
       
  1001     this.contents[_listId].addElements(_contents);
       
  1002 }
       
  1003 
       
  1004 Model.Source.prototype.getList = function(_listId, _global) {
       
  1005     _global = (typeof _global !== "undefined" && _global);
       
  1006     if (_global) {
       
  1007         return this.directory.getGlobalList().filter(function(_e) {
       
  1008             return (_e.elementType === _listId);
       
  1009         });
       
  1010     } else {
       
  1011         return this.contents[_listId] || new IriSP.Model.List(this.directory);
       
  1012     }
       
  1013 }
       
  1014 
       
  1015 Model.Source.prototype.forEach = function(_callback) {
       
  1016     var _this = this;
       
  1017     ns._(this.contents).forEach(function(_value, _key) {
       
  1018         _callback.call(_this, _value, _key);
       
  1019     })
       
  1020 }
       
  1021 
       
  1022 Model.Source.prototype.getElement = function(_elId) {
       
  1023     return this.directory.getElement(_elId);
       
  1024 }
       
  1025 
       
  1026 Model.Source.prototype.get = function() {
       
  1027     this.status = Model._SOURCE_STATUS_WAITING;
       
  1028     this.handleCallbacks();
       
  1029 }
       
  1030 
       
  1031 /* We defer the callbacks calls so they execute after the queue is cleared */
       
  1032 Model.Source.prototype.deferCallback = function(_callback) {
       
  1033     var _this = this;
       
  1034     ns._.defer(function() {
       
  1035         _callback.call(_this);
       
  1036     });
       
  1037 }
       
  1038 
       
  1039 Model.Source.prototype.handleCallbacks = function() {
       
  1040     this.status = Model._SOURCE_STATUS_READY;
       
  1041     while (this.callbackQueue.length) {
       
  1042         this.deferCallback(this.callbackQueue.splice(0,1)[0]);
       
  1043     }
       
  1044 }
       
  1045 Model.Source.prototype.onLoad = function(_callback) {
       
  1046     if (this.status === Model._SOURCE_STATUS_READY) {
       
  1047         this.deferCallback(_callback);
       
  1048     } else {
       
  1049         this.callbackQueue.push(_callback);
       
  1050     }
       
  1051 }
       
  1052 
       
  1053 Model.Source.prototype.serialize = function() {
       
  1054     return this.serializer.serialize(this);
       
  1055 }
       
  1056 
       
  1057 Model.Source.prototype.deSerialize = function(_data) {
       
  1058     this.serializer.deSerialize(_data, this);
       
  1059 }
       
  1060 
       
  1061 Model.Source.prototype.getAnnotations = function(_global) {
       
  1062     _global = (typeof _global !== "undefined" && _global);
       
  1063     return this.getList("annotation", _global);
       
  1064 }
       
  1065 
       
  1066 Model.Source.prototype.getMedias = function(_global) {
       
  1067     _global = (typeof _global !== "undefined" && _global);
       
  1068     return this.getList("media", _global);
       
  1069 }
       
  1070 
       
  1071 Model.Source.prototype.getTags = function(_global) {
       
  1072     _global = (typeof _global !== "undefined" && _global);
       
  1073     return this.getList("tag", _global);
       
  1074 }
       
  1075 
       
  1076 Model.Source.prototype.getMashups = function(_global) {
       
  1077     _global = (typeof _global !== "undefined" && _global);
       
  1078     return this.getList("mashup", _global);
       
  1079 }
       
  1080 
       
  1081 Model.Source.prototype.getAnnotationTypes = function(_global) {
       
  1082     _global = (typeof _global !== "undefined" && _global);
       
  1083     return this.getList("annotationType", _global);
       
  1084 }
       
  1085 
       
  1086 Model.Source.prototype.getAnnotationsByTypeTitle = function(_title, _global) {
       
  1087     _global = (typeof _global !== "undefined" && _global);
       
  1088     var _res = new Model.List(this.directory),
       
  1089         _annTypes = this.getAnnotationTypes(_global).searchByTitle(_title);
       
  1090     _annTypes.forEach(function(_annType) {
       
  1091         _res.addElements(_annType.getAnnotations(_global));
       
  1092     })
       
  1093     return _res;
       
  1094 }
       
  1095 
       
  1096 Model.Source.prototype.getDuration = function() {
       
  1097     var _m = this.currentMedia;
       
  1098     if (typeof _m !== "undefined") {
       
  1099         return this.currentMedia.duration;
       
  1100     }
       
  1101 }
       
  1102 
       
  1103 Model.Source.prototype.getCurrentMedia = function(_opts) {
       
  1104     if (typeof this.currentMedia === "undefined") {
       
  1105         if (_opts.is_mashup) {
       
  1106             var _mashups = this.getMashups();
       
  1107             if (_mashups.length) {
       
  1108                 this.currentMedia = _mashups[0];
       
  1109             }
       
  1110         } else {
       
  1111             var _medias = this.getMedias();
       
  1112             if (_medias.length) {
       
  1113                 this.currentMedia = _medias[0];
       
  1114             }
       
  1115         }
       
  1116     }
       
  1117     return this.currentMedia;
       
  1118 }
       
  1119 
       
  1120 Model.Source.prototype.merge = function(_source) {
       
  1121     var _this = this;
       
  1122     _source.forEach(function(_value, _key) {
       
  1123         _this.getList(_key).addElements(_value);
       
  1124     });
       
  1125 }
       
  1126 
       
  1127 /* */
       
  1128 
       
  1129 Model.RemoteSource = function(_config) {
       
  1130     Model.Source.call(this, _config);
       
  1131 }
       
  1132 
       
  1133 Model.RemoteSource.prototype = new Model.Source();
       
  1134 
       
  1135 Model.RemoteSource.prototype.get = function() {
       
  1136     this.status = Model._SOURCE_STATUS_WAITING;
       
  1137     var _this = this,
       
  1138         urlparams = this.url_params || {},
       
  1139         dataType = (Model.isLocalURL(this.url) ? "json" : "jsonp");
       
  1140     urlparams.format = dataType;
       
  1141     ns.jQuery.ajax({
       
  1142         url: this.url,
       
  1143         dataType: dataType,
       
  1144         data: urlparams,
       
  1145         traditional: true,
       
  1146         success: function(_result) {
       
  1147             _this.deSerialize(_result);
       
  1148             _this.handleCallbacks();
       
  1149         }
       
  1150     });
       
  1151 }
       
  1152 
       
  1153 /* */
       
  1154 
       
  1155 Model.Directory = function() {
       
  1156     this.remoteSources = {};
       
  1157     this.elements = {};
       
  1158 }
       
  1159 
       
  1160 Model.Directory.prototype.remoteSource = function(_properties) {
       
  1161     if (typeof _properties !== "object" || typeof _properties.url === "undefined") {
       
  1162         throw "Error : Model.Directory.remoteSource(configuration): configuration.url is undefined";
       
  1163     }
       
  1164     var _config = ns._({ directory: this }).extend(_properties);
       
  1165     _config.url_params = _config.url_params || {};
       
  1166     var _hash = _config.url + "?" + ns.jQuery.param(_config.url_params);
       
  1167     if (typeof this.remoteSources[_hash] === "undefined") {
       
  1168         this.remoteSources[_hash] = new Model.RemoteSource(_config);
       
  1169     }
       
  1170     return this.remoteSources[_hash];
       
  1171 }
       
  1172 
       
  1173 Model.Directory.prototype.newLocalSource = function(_properties) {
       
  1174     var _config = ns._({ directory: this }).extend(_properties),
       
  1175         _res = new Model.Source(_config);
       
  1176     return _res;
       
  1177 }
       
  1178 
       
  1179 Model.Directory.prototype.getElement = function(_id) {
       
  1180     return this.elements[_id];
       
  1181 }
       
  1182 
       
  1183 Model.Directory.prototype.addElement = function(_element) {
       
  1184     this.elements[_element.id] = _element;
       
  1185 }
       
  1186 
       
  1187 Model.Directory.prototype.getGlobalList = function() {
       
  1188     var _res = new Model.List(this);
       
  1189     _res.addIds(ns._(this.elements).keys());
       
  1190     return _res;
       
  1191 }
       
  1192 
       
  1193 return Model;
       
  1194 
       
  1195 })(IriSP);
       
  1196 
       
  1197 /* END model.js */
       
  1198