diff -r cfcbac34d020 -r 97fef7a4b189 src/js/model.js --- a/src/js/model.js Fri Nov 16 17:36:56 2012 +0100 +++ b/src/js/model.js Wed Nov 21 16:33:51 2012 +0100 @@ -1,7 +1,6 @@ /* TODO: Separate Project-specific data from Source */ /* model.js is where data is stored in a standard form, whatever the serializer */ - IriSP.Model = (function (ns) { function pad(n, x, b) { @@ -18,7 +17,21 @@ } var uidbase = rand16(8) + "-" + rand16(4) + "-", uidincrement = Math.floor(Math.random()*0x10000); - + + var charsub = [ + [ 'a', 'á', 'à', 'â', 'ä' ], + [ 'c', 'ç' ], + [ 'e', 'é', 'è', 'ê', 'ë' ], + [ 'i', 'í', 'ì', 'î', 'ï' ], + [ 'o', 'ó', 'ò', 'ô', 'ö' ] + ]; + + var removeChars = [ + String.fromCharCode(768), String.fromCharCode(769), String.fromCharCode(770), String.fromCharCode(771), String.fromCharCode(807), + "{", "}", "(", ")", "[", "]", "【", "】", "、", "・", "‥", "。", "「", "」", "『", "』", "〜", ":", "!", "?", " ", + ",", " ", ";", "(", ")", ".", "*", "+", "\\", "?", "|", "{", "}", "[", "]", "^", "#", "/" + ] + var Model = { _SOURCE_STATUS_EMPTY : 0, _SOURCE_STATUS_WAITING : 1, @@ -26,6 +39,13 @@ getUID : function() { return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6); }, + isLocalURL : function(url) { + var matches = url.match(/^(\w+:)\/\/([^/]+)/); + if (matches) { + return(matches[1] === document.location.protocol && matches[2] === document.location.host) + } + return true; + }, regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) { var _testOnly = _testOnly || false, _iexact = _iexact || false; @@ -46,6 +66,30 @@ } return new RegExp( _source, _flags); }, + fullTextRegexps: function(_text) { + var remsrc = "[\\" + removeChars.join("\\") + "]", + remrx = new RegExp(remsrc,"gm"), + txt = _text.toLowerCase().replace(remrx,"") + res = [], + charsrc = ns._(charsub).map(function(c) { + return "(" + c.join("|") + ")"; + }), + charsrx = ns._(charsrc).map(function(c) { + return new RegExp(c); + }), + src = ""; + for (var j = 0; j < txt.length; j++) { + if (j) { + src += remsrc + "*"; + } + var l = txt[j]; + ns._(charsrc).each(function(v, k) { + l = l.replace(charsrx[k], v); + }); + src += l; + } + return "(" + src + ")"; + }, isoToDate : function(_str) { // http://delete.me.uk/2005/03/iso8601.html 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})))?)?)?)?"; @@ -71,7 +115,8 @@ _res.setTime(Number(time)); return _res; }, - dateToIso : function(d) { + dateToIso : function(_d) { + var d = _d ? new Date(_d) : new Date(); return d.getUTCFullYear()+'-' + pad(2, d.getUTCMonth()+1)+'-' + pad(2, d.getUTCDate())+'T' @@ -93,6 +138,15 @@ console.trace(); throw "Error : new Model.List(directory): directory is undefined"; } + var _this = this; + this.on("clear-search", function() { + _this.searching = false; + _this.regexp = undefined; + _this.forEach(function(_element) { + _element.found = undefined; + }); + _this.trigger("search-cleared"); + }) } Model.List.prototype = new Array(); @@ -191,6 +245,28 @@ }); } +Model.List.prototype.search = function(_text) { + if (!_text) { + this.trigger("clear-search"); + return this; + } + this.searching = true; + this.trigger("search", _text); + var rxsource = Model.fullTextRegexps(_text) + rgxp = new RegExp(rxsource,"im"), + this.regexp = new RegExp(rxsource,"gim"); + var res = this.filter(function(_element, _k) { + var titlematch = rgxp.test(_element.title), + descmatch = rgxp.test(_element.description), + _isfound = !!(titlematch || descmatch); + _element.found = _isfound; + _element.trigger(_isfound ? "found" : "not-found"); + return _isfound; + }); + this.trigger(res.length ? "found" : "not-found",res); + return res; +} + Model.List.prototype.getTitles = function() { return this.map(function(_el) { return _el.title; @@ -297,16 +373,16 @@ } Model.Time.prototype.setMilliseconds = function(_milliseconds) { - var _ante = _milliseconds; + var _ante = this.milliseconds; switch(typeof _milliseconds) { case "string": - this.milliseconds = parseFloat(_milliseconds); + this.milliseconds = parseInt(_milliseconds); break; case "number": - this.milliseconds = _milliseconds; + this.milliseconds = Math.floor(_milliseconds); break; case "object": - this.milliseconds = parseFloat(_milliseconds.valueOf()); + this.milliseconds = parseInt(_milliseconds.valueOf()); break; default: this.milliseconds = 0; @@ -329,7 +405,8 @@ return { hours : Math.floor(_totalSeconds / 3600), minutes : (Math.floor(_totalSeconds / 60) % 60), - seconds : _totalSeconds % 60 + seconds : _totalSeconds % 60, + milliseconds: this.milliseconds % 1000 } } @@ -341,13 +418,16 @@ return this.milliseconds; } -Model.Time.prototype.toString = function() { +Model.Time.prototype.toString = function(showCs) { var _hms = this.getHMS(), _res = ''; if (_hms.hours) { _res += _hms.hours + ':' } _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds); + if (showCs) { + _res += "." + Math.round(_hms.milliseconds / 100) + } return _res; } @@ -394,18 +474,20 @@ Model.Element = function(_id, _source) { this.elementType = 'element'; + this.title = ""; + this.description = ""; + this.__events = {} if (typeof _source === "undefined") { return; } if (typeof _id === "undefined" || !_id) { _id = Model.getUID(); } + this.id = _id; this.source = _source; - this.id = _id; - this.title = ""; - this.description = ""; - this.__events = {} - this.source.directory.addElement(this); + if (_source !== this) { + this.source.directory.addElement(this); + } } Model.Element.prototype.toString = function() { @@ -474,6 +556,20 @@ }); this.on("timeupdate", function(_time) { _this.currentTime = _time; + _this.getAnnotations().filter(function(_a) { + return (_a.end <= _time || _a.begin > _time) && _a.playing + }).forEach(function(_a) { + _a.playing = false; + _a.trigger("leave"); + _this.trigger("leave-annotation",_a); + }); + _this.getAnnotations().filter(function(_a) { + return _a.begin <= _time && _a.end > _time && !_a.playing + }).forEach(function(_a) { + _a.playing = true; + _a.trigger("enter"); + _this.trigger("enter-annotation",_a); + }); }); } @@ -515,6 +611,9 @@ this.trigger("setpause"); } +Model.Playable.prototype.show = function() {} + +Model.Playable.prototype.hide = function() {} /* */ @@ -523,22 +622,7 @@ this.elementType = 'media'; this.duration = new Model.Time(); this.video = ''; - var _this = this; - this.on("timeupdate", function(_time) { - _this.getAnnotations().filter(function(_a) { - return (_a.end <= _time || _a.begin > _time) && _a.playing - }).forEach(function(_a) { - _a.playing = false; - _a.trigger("leave"); - }); - _this.getAnnotations().filter(function(_a) { - return _a.begin <= _time && _a.end > _time && !_a.playing - }).forEach(function(_a) { - _a.playing = true; - _a.trigger("enter"); - }); - }); } Model.Media.prototype = new Model.Playable(); @@ -610,11 +694,17 @@ Model.Annotation.prototype.setBegin = function(_beginMs) { this.begin.setMilliseconds(Math.max(0,_beginMs)); this.trigger("change-begin"); + if (this.end < this.begin) { + this.setEnd(this.begin); + } } Model.Annotation.prototype.setEnd = function(_endMs) { - this.end.setMilliseconds(Math.min(_endMs)); + this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds)); this.trigger("change-end"); + if (this.end < this.begin) { + this.setBegin(this.end); + } } Model.Annotation.prototype.setDuration = function(_durMs) { @@ -659,8 +749,9 @@ Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source); this.elementType = 'mashedAnnotation'; this.annotation = _annotation; - this.begin = new Model.Time(_mashup.duration); - this.end = new Model.Time(_mashup.duration + _annotation.getDuration()); + this.begin = new Model.Time(); + this.end = new Model.Time(); + this.duration = new Model.Time(); this.title = this.annotation.title; this.description = this.annotation.description; this.color = this.annotation.color; @@ -668,6 +759,12 @@ this.on("click", function() { _mashup.setCurrentTime(_this.begin); }); + this.on("enter", function() { + _this.annotation.trigger("enter"); + }); + this.on("leave", function() { + _this.annotation.trigger("leave"); + }); } Model.MashedAnnotation.prototype = new Model.Element(null); @@ -692,6 +789,12 @@ return this.annotation.getDuration(); } +Model.MashedAnnotation.prototype.setBegin = function(_begin) { + this.begin.setMilliseconds(_begin); + this.duration.setMilliseconds(this.annotation.getDuration()); + this.end.setMilliseconds(_begin + this.duration); +} + /* */ Model.Mashup = function(_id, _source) { @@ -699,55 +802,145 @@ this.elementType = 'mashup'; this.duration = new Model.Time(); this.segments = new Model.List(_source.directory); - this.medias = new Model.List(_source.directory); - var _currentMedia = null; + this.loaded = false; var _this = this; - this.on("timeupdate", function(_time) { - _this.getAnnotations().filter(function(_a) { - return (_a.end <= _time || _a.begin > _time) && _a.playing - }).forEach(function(_a) { - _a.playing = false; - _a.trigger("leave"); - }); - _this.getAnnotations().filter(function(_a) { - return _a.begin <= _time && _a.end > _time && !_a.playing - }).forEach(function(_a) { - _a.playing = true; - _a.trigger("enter"); - var _m = _a.getMedia(); - if (_m !== _currentMedia) { - if (_currentMedia) { - _currentMedia.trigger("leave"); - } - _m.trigger("enter"); - _currentMedia = _m; - } - }); - }); + this._updateTimes = function() { + _this.updateTimes(); + _this.trigger("change"); + } + this.on("add", this._updateTimes); + this.on("remove", this._updateTimes); } Model.Mashup.prototype = new Model.Playable(); -Model.Mashup.prototype.addSegment = function(_annotation) { - var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation); - this.duration.setMilliseconds(_mashedAnnotation.end); +Model.Mashup.prototype.checkLoaded = function() { + var loaded = !!this.segments.length; + this.getMedias().forEach(function(_m) { + loaded = loaded && _m.loaded; + }); + this.loaded = loaded; + if (loaded) { + this.trigger("loadedmetadata"); + } +} + +Model.Mashup.prototype.updateTimes = function() { + var _time = 0; + this.segments.forEach(function(_segment) { + _segment.setBegin(_time); + _time = _segment.end; + }); + this.duration.setMilliseconds(_time); +} + +Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) { + var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation), + _defer = _defer || false; this.segments.push(_mashedAnnotation); - this.medias.push(_annotation.getMedia()); + _annotation.on("change-begin", this._updateTimes); + _annotation.on("change-end", this._updateTimes); + if (!_defer) { + this.trigger("add"); + } +} + +Model.Mashup.prototype.addAnnotationById = function(_elId, _defer) { + var _annotation = this.source.getElement(_elId), + _defer = _defer || false; + if (typeof _annotation !== "undefined") { + this.addAnnotation(_annotation, _defer); + } +} + +Model.Mashup.prototype.addAnnotations = function(_segments) { + var _this = this; + ns._(_segments).forEach(function(_segment) { + _this.addAnnotation(_segment, true); + }); + this.trigger("add"); } -Model.Mashup.prototype.addSegmentById = function(_elId) { - var _annotation = this.source.getElement(_elId); - if (typeof _annotation !== "undefined") { - this.addSegment(_annotation); +Model.Mashup.prototype.addAnnotationsById = function(_segments) { + var _this = this; + ns._(_segments).forEach(function(_segment) { + _this.addAnnotationById(_segment, true); + }); + this.trigger("add"); +} + +Model.Mashup.prototype.removeAnnotation = function(_annotation, _defer) { + var _defer = _defer || false; + _annotation.off("change-begin", this._updateTimes); + _annotation.off("change-end", this._updateTimes); + this.segments.removeId(this.id + "_" + _annotation.id); + if (!_defer) { + this.trigger("remove"); + } +} + +Model.Mashup.prototype.removeAnnotationById = function(_annId, _defer) { + var _defer = _defer || false; + var _annotation = this.source.getElement(_annId); + + if (_annotation) { + this.removeAnnotation(_annotation, _defer); + } + if (!_defer) { + this.trigger("remove"); } } +Model.Mashup.prototype.setAnnotations = function(_segments) { + while (this.segments.length) { + this.removeAnnotation(this.segments[0].annotation, true); + } + this.addAnnotations(_segments); +} + +Model.Mashup.prototype.setAnnotationsById = function(_segments) { + while (this.segments.length) { + this.removeAnnotation(this.segments[0].annotation, true); + } + this.addAnnotationsById(_segments); +} + +Model.Mashup.prototype.hasAnnotation = function(_annotation) { + return !!ns._(this.segments).find(function(_s) { + return _s.annotation === _annotation + }); +} + +Model.Mashup.prototype.getAnnotation = function(_annotation) { + return ns._(this.segments).find(function(_s) { + return _s.annotation === _annotation + }); +} + +Model.Mashup.prototype.getAnnotationById = function(_id) { + return ns._(this.segments).find(function(_s) { + return _s.annotation.id === _id + }); +} + Model.Mashup.prototype.getAnnotations = function() { return this.segments; } +Model.Mashup.prototype.getOriginalAnnotations = function() { + var annotations = new Model.List(this.source.directory); + this.segments.forEach(function(_s) { + annotations.push(_s.annotation); + }); + return annotations; +} + Model.Mashup.prototype.getMedias = function() { - return this.medias; + var medias = new Model.List(this.source.directory); + this.segments.forEach(function(_annotation) { + medias.push(_annotation.getMedia()) + }) + return medias; } Model.Mashup.prototype.getAnnotationsByTypeTitle = function(_title) { @@ -784,6 +977,7 @@ /* */ Model.Source = function(_config) { + Model.Element.call(this, false, this); this.status = Model._SOURCE_STATUS_EMPTY; this.elementType = "source"; if (typeof _config !== "undefined") { @@ -939,10 +1133,18 @@ Model.RemoteSource.prototype.get = function() { this.status = Model._SOURCE_STATUS_WAITING; - var _this = this; - this.serializer.loadData(this.url, function(_result) { - _this.deSerialize(_result); - _this.handleCallbacks(); + var _this = this, + urlparams = this.url_params || {}, + dataType = (Model.isLocalURL(this.url) ? "json" : "jsonp"); + urlparams.format = dataType; + ns.jQuery.ajax({ + url: this.url, + dataType: dataType, + data: urlparams, + success: function(_result) { + _this.deSerialize(_result); + _this.handleCallbacks(); + } }); } @@ -958,10 +1160,12 @@ throw "Error : Model.Directory.remoteSource(configuration): configuration.url is undefined"; } var _config = ns._({ directory: this }).extend(_properties); - if (typeof this.remoteSources[_properties.url] === "undefined") { - this.remoteSources[_properties.url] = new Model.RemoteSource(_config); + _config.url_params = _config.url_params || {}; + var _hash = _config.url + "?" + ns.jQuery.param(_config.url_params); + if (typeof this.remoteSources[_hash] === "undefined") { + this.remoteSources[_hash] = new Model.RemoteSource(_config); } - return this.remoteSources[_properties.url]; + return this.remoteSources[_hash]; } Model.Directory.prototype.newLocalSource = function(_properties) {