--- 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) {