File Index
+ + +LdtPlayer-release.js
+ +-
+
+
+
+
+
+ + +
# HG changeset patch # User veltr # Date 1333724134 -7200 # Node ID 75ba6645723283574e4151b8a67592b584a913cb # Parent 7b55777486c3486599831c49cee8af8dae8f26d7# Parent 4ae2247a59f41fc00c41431cd4bce7495ef5dfaa Merge with 4ae2247a59f41fc00c41431cd4bce7495ef5dfaa diff -r 7b55777486c3 -r 75ba66457232 .hgignore --- a/.hgignore Tue Oct 04 11:16:06 2011 +0200 +++ b/.hgignore Fri Apr 06 16:55:34 2012 +0200 @@ -4,3 +4,7 @@ syntax: glob build/LdtPlayer-release.js build/LdtPlayer.min.js +*.swp +*.orig +src/js/site.js +doc/jsdoc/* diff -r 7b55777486c3 -r 75ba66457232 .settings/org.eclipse.php.core.prefs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.php.core.prefs Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,3 @@ +#Sat Sep 24 15:42:33 CEST 2011 +eclipse.preferences.version=1 +include_path= diff -r 7b55777486c3 -r 75ba66457232 assets/annotate.psd Binary file assets/annotate.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/annotate_arrow.psd Binary file assets/annotate_arrow.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/arrow.psd Binary file assets/arrow.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/fullscreen.psd Binary file assets/fullscreen.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/gplus_button.psd Binary file assets/gplus_button.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/left_edge_arrow.psd Binary file assets/left_edge_arrow.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/left_handle.psd Binary file assets/left_handle.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/minimize.psd Binary file assets/minimize.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/mute.psd Binary file assets/mute.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/pause_sprite.psd Binary file assets/pause_sprite.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/play.psd Binary file assets/play.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/polemic_buttons_sprite.psd Binary file assets/polemic_buttons_sprite.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/profile_arrow.psd Binary file assets/profile_arrow.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/reply.psd Binary file assets/reply.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/retweet.psd Binary file assets/retweet.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/right_edge_arrow.psd Binary file assets/right_edge_arrow.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/right_handle.psd Binary file assets/right_handle.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/search.psd Binary file assets/search.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/sound.psd Binary file assets/sound.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/submit_annotation.psd Binary file assets/submit_annotation.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/tweet_button.psd Binary file assets/tweet_button.psd has changed diff -r 7b55777486c3 -r 75ba66457232 assets/wire_pattern.psd Binary file assets/wire_pattern.psd has changed diff -r 7b55777486c3 -r 75ba66457232 doc/LdtPlayer-allocine.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/LdtPlayer-allocine.js Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,80 @@ +/* exemple de code pour ajouter le support d'un autre player flash. + Le code était fait à la base pour allociné, étendez le si vous en + avez besoin. + */ + +/* Il faut étendre cette classe pour que le metadataplayer supporte le player + allocine. Pour l'instant, le code présent est celui pour le jwplayer + */ +IriSP.PopcornReplacement.allocine = function(container, options) { + /** + * Ce constructeur reçoit deux paramètres : + * - container est une chaine de caractère indiquant l'id du div dans lequel il + * doit s'initialiser + * - options est un dictionnaire contenant les options de configuration du player + * (correspond à la partie de configuration du player dans polemic.htm) + */ + + /** + appel du parent pour initialiser les structures communes à tous les players - + obligatoire. + */ + IriSP.PopcornReplacement.player.call(this, container, options); + + this.media.duration = options.duration; /* optionnel */ + + /* Préservation de this, au cas où */ + var _this = this; + + /** Déclaration au player des fonctions que l'api flash expose - brièvement: + * - play et pause ne prennent pas de paramètres + * - lorsque le metadataplayer appelle getPosition, le player flash doit retourner + * la durée depuis le début en secondes, + * - seek reçoit en paramètre la position en secondes depuis le début de la + * vidéo vers laquelle on veut seeker. + * - getMute retourne true si le player est muté et false sinon + * - setMute prend un paramètre. Si celui-ci est true la vidéo doit etre mutée, + * sinon le son doit être activé. + * + * NB: les valeurs de retour ne sont utilisés quand pour getPosition et getMute. + */ + this.playerFns = { + play: function() { return jwplayer(this.container).play(); }, + pause: function() { return jwplayer(this.container).pause(); }, + getPosition: function() { return jwplayer(this.container).getPosition(); }, + seek: function(pos) { return jwplayer(this.container).seek(pos); }, + getMute: function() { return jwplayer(this.container).getMute() }, + setMute: function(p) { return jwplayer(this.container).setMute(p); } + } + + /* Déclaration des callbacks au jwplayer - ces callbacks sont appelés par le + * player flash au moment où les évenements correspondants sont declenchés. + * le dictionnaire this.callbacks + * contient cinq entrées : onReady, onTime, onPlay, onPause, onSeek. + * + * - onReady est une fonction qui ne prend pas de paramètres et qui est appellée + * quand le player flash a fini de s'initialiser. + * - onTime est appelée périodiquement (par ex, toutes les demi-secondes). Elle + * reçoit en paramètre un dictionnaire qui doit contenir un champ nommé position + * qui contient le temps écoulé depuis le début de la vidéo. + * - onPlay est appelé quand le player commence ou reprend la lecture. Le callback + * ne prend pas de paramètres. + * - onPause est appellé quand le player entre en état pausé. Le callback ne prend + * pas de paramètres. + * - onSeek est appelé quand le player flash seeke une vidéo. Il reçoit en + * paramètre un object contenant deux entrées : + * - position: la position en secondes depuis le début de la vidéo au moment où l'on seeke + * - offset: la position cible en secondes depuis le début de la vidéo. + * + * Pour réference, voici la doc des évenements proposés par le jwplayer : + * http://www.longtailvideo.com/support/jw-player/jw-player-for-flash-v5/12540/javascript-api-reference#Events + */ + options.events = this.callbacks; + + /* initialisation du player dans le div qui possède l'id this.container - + a remplacer par un appel à swfobject par exemple */ + jwplayer(this.container).setup(options); +}; + +/* Obligatoire pour l'héritage - ne pas modifier */ +IriSP.PopcornReplacement.allocine.prototype = new IriSP.PopcornReplacement.player("", {}); \ No newline at end of file diff -r 7b55777486c3 -r 75ba66457232 doc/code.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/code.txt Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,52 @@ +Metadataplayer : Code et structure +================================== + +Ce document présente briévement la conception du metadataplayer. + +Compilation +=========== + +Pour le moment, il est nécessaire d'executer un script pour génerer le fichier final. Ce script est un script ant, accessible dans +sbin/build/client.xml. Des raccourcis existent comme compil.bat (win) et compil.sh (unix). + +L'étape de compilation est nécessaire pour l'instant car on a des templates qui sont integrées au fichier final à ce moment là. + +Objets +====== + +En gros, nous avons quatre objets: +- le DataLoader, qui est l'objet chargé de récupérer les données extérieures et de les cacher +- le sérializeur. C'est une classe derivée de IriSP.Serializer et qui implémente des méthodes pour sérializer/déserializer des données. + Ils sont définis dans le répertoire serializers/ +- le layout manager, un objet qui gère un div et qui permet de créer/supprimer des sous-div. +- les widgets. Un widget est associé à un div. Il possède également une réference sur l'objet popcorn pour pouvoir envoyer et recevoir des + messages. Il doit possèder impérativement deux méthodes : un constructeur, qui appelle le constructeur de l'object Widget, et une méthode + draw(), qui initialise l'objet. Il peut aussi optionnellement définir une méthod redraw(), au cas où le widget aurait besoin d'être redessiné. + +Templates +========= + +Le player utilise des templates qui sont transformées en chaînes javascript puis inserées dans le fichier final. +Un template nommé truc.html est accessible dans le code sous le nom : IriSP.truc_template. + +ATTENTION: Une template ne doit en aucun cas contenir de guillements ("), sous peine de faire planter la compilation et le fichier géneré. + +Initialisation +============== + +Le code d'initialisation est défini dans init.js. En gros, il se contente de parser la structure IriSP.config qui définit les options génerales +du player et les widgets à afficher. Chaque widget peut avoir des widgets de dépendances, qui seront instanciés avant lui et dont il aura accès +(voir l'annexe idiomes) +Pour plus d'informations, voir le code source des tests d'integration, notamment le fichier integration/polemic.htm. + + +Annexe: Idiomes +=============== + +Le code utilise quelques idiomes. Les voici, sans classement particulier : +- les membres de données privées d'un objet sont précedés d'un underscore. Ils ne sont pas censés être manipulés par des objets extérieurs, hormis + par les tests unitaires. +- tous les widgets et certains autres objets possèdent un attribut selector, qui est en fait une instance de jQuery appellée sur le div contenant + l'objet en question. +- certains widgets qui possèdent des dépendances ont accès à l'objet dépendant en tant qu'attribut portant le nom du widget en + question. Ainsi, comme le PolemicWidget possède comme dépendance un TooltipWidget, il peut accèder à sa dépendance sous "this.TooltipWidget". diff -r 7b55777486c3 -r 75ba66457232 doc/implementer-un-player.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/implementer-un-player.txt Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,21 @@ +API qu'un player doit implémenter pour espérer être supporté facilement par le +metadataplayer : + +En gros, il faut une api similaire à celle du jwplayer +(cf : http://www.longtailvideo.com/support/jw-player/jw-player-for-flash-v5/12540/javascript-api-reference). + +Cependant on n'a besoin que des fonctions suivantes : +- play() - sans argument. Met le player en lecture, s'il était en pause, et ne fait rien autrement +- pause - sans arguments. Pause le player s'il était en train de lire +- seek(duree_secondes) - Met la vidéo à duree_secondes depuis le début +- getMute - sans arguments. Retourne true si le player est muté, false sinon +- setMute - prend un booléen comme argument, coupe le son s'il est à true, et l'active s'il est à false +- getPosition - sans arguments. Retourne le nombre de seconds ecoulées depuis le début del + la vidéo + +On a également besoin que le player ait des callbacks pour les évenements suivants : +- quand le lecteur a fini d'être initialisé (onReady pour jwplayer) +- declenché à chaque fois que le player change d'image (onTime) +- declenché quand la lecture de la vidéo commence (onPlay) +- declenché quand la vidéo est mise sur pause (onPause) +- declenché quand l'utilisateur seek (onSeek) \ No newline at end of file diff -r 7b55777486c3 -r 75ba66457232 doc/signals.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/signals.txt Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,83 @@ +This is the list of the signals sent by the different widgets : + +Mediafragments +============== + IriSP.Mediafragment.hashchange(hashvalue): This message is used by the iframe embed code to relay data + outside of the iframe. This message sends as a parameter the + new anchor value of the url. + + IriSP.Mediafragment.showAnnotation(annotationId): This message is sent when the mediafragment code + has fast-forwarded to the beginning of an annotation + specified in the url. The subscribing widgets receive + the id of the annotation and can highlight it if they + want. + +PlayerWidget +============ + IriSP.PlayerWidget.AnnotateButton.clicked: sent whenever the "Annotate" button is clicked on the player widget. + IriSP.PlayerWidget.MouseOver: sent when the mouse is hovering the widget. Used to maximize the sliderWidget to + get a behaviour similar to youtube. + IriSP.PlayerWidget.MouseOut: sent when the mouse has left the widget. + + IriSP.search.open: sent when the searchbox has been opened + IriSP.search.closed: sent when the searchbox has been closed + IriSP.search(searchQuery): sent whenever the user presses a key in the searchfield. Param searchQuery + holds the contents of the search field. + + IriSP.search.triggeredSearch(searchText): triggered by other widgets when they want to launch a new search. searchText is the search term. + IriSP.search.noMatchFound: triggered by other widgets to indicate that they've not found content matching the search terms. + IriSP.search.matchFound: triggered by other widgets to indicate that they've found content matching the search terms. + Note: the PlayerWidget changes the highlighting of the search box depending on if it has received these messages. + +ArrowWidget +=========== + IriSP.ArrowWidget.blockArrow: prevent the ArrowWidget from moving automatically + IriSP.ArrowWidget.releaseArrow: restore the ArrowWidget to its non-blocked state + +SliceWidget +=========== + + IriSP.SliceWidget.hide: sent to the SliceWidget. hides the widget. + IriSP.SliceWidget.show: sent to the SliceWidget. shows the widget. + IriSP.SliceWidget.position, [left, width]: sent to the SliceWidget. Param is an array with the + left position and the width of the zone, in percents. + IriSP.SliceWidget.zoneChange [left, width]: sent by the widget whenever the user moves the widget. The parameter + is an array with the left position of the zone its width. Both values + are in percents. + +AnnotationWidget +================ + + IriSP.AnnotationsWidget.show: received by the AnnotationsWidget. hides the widget. + IriSP.AnnotationsWidget.hide: received by the AnnotationsWidget. shows the widget. + + +CreateAnnotationWidget +====================== + + IriSP.createAnnotationWidget.addedAnnotation(annotation): sent by the widget when an annotation has been created. + passes the annotation dict as parameter. + +PolemicWidget +============= + + IriSP.PolemicTweet.click(elementId): sent by the widget when the user clicks on it. Passes the annotation id as a param. + the mediafragment modules listens to this message to update the url accordingly. + +SegmentsWidget +============== + + IriSP.SegmentsWidget.click(annotationId): sent by the widget when the user clicks on it. Passes the annotation id as a param. + the mediafragment modules listens to this message to update the url accordingly. + +SparklineWidget +=============== + + IriSP.SparklineWidget.clicked(newTime): sent whenever the sparkline widget is clicked. Param newTime is the new time the video + is seeking too. The mediafragment module listens to this. + +StackGraphWidget +================= + + IriSP.StackGraphWidget.clicked(newTime): sent whenever the stackgraph widget is clicked. Param newTime is the new time the video + is seeking too. The mediafragment module listens to this. \ No newline at end of file diff -r 7b55777486c3 -r 75ba66457232 doc/widget_tutorial/LdtPlayer-tutorial.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/widget_tutorial/LdtPlayer-tutorial.js Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,61 @@ +/* This is the constructor of the widget. It's called by the + initialization routine. +*/ +IriSP.TutorialWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + /* After having called the parent constructor, a couple objects are defined for us + this._config contains all the configuration options passed in the config. + this._id holds the id of the div where the widget has to draw himself + this._serializer is an object containing the metadata that was request in the configuration + options. + */ + +} + +/* We need to create assign new prototype to TutorialWidget.prototype + because we're going to declare methods in it */ +IriSP.TutorialWidget.prototype = new IriSP.Widget(); + +/* This method draws the widget - it's called automatically by + the initialization script. + */ +IriSP.TutorialWidget.prototype.draw = function() { + /* this.selector is a shortcut to jQuery(widget.container) - it's used everywhere in the code */ + this.selector.html('Hello'); + this.selector.css({ + "text-align" : "center", + "padding": "10px 0", + "font-size" : "14px" + }); + + /* The following is a list of idioms found throughout the code */ + var templ = IriSP.player_template; /* get the compiled template code for the player.html template - + templates are located in the src/templates directory and are automatically + compiled and made available in the compiled file as IriSP.templatename_template (without the .html) + */ + var res = IriSP.templToHTML(IriSP.player_template, {var: 1}); /* format the template with the variable 'var' */ + + /* this._Popcorn is a handle on the Popcorn object. It exposes the API which is documented + here : http://popcornjs.org/api + currentTime is a Popcorn method that either returns or changes the currentTime. + */ + var time = this._Popcorn.currentTime(); + + /* Listen to the IriSP.TutorialWidget.foo message. By convention, the name of + a message is IriSP.widgetName.messageName */ + this._Popcorn.listen("IriSP.TutorialWidget.foo", + /* IriSP.wrap preserves this in the callback */ + IriSP.wrap(this, this.fooMessageHandler)); + /* send a message, passing an object allong */ + this._Popcorn.trigger("IriSP.TutorialWidget.foo", {name: "Dave", surname: "Grohl"}); +}; + +/* Handler for the IriSP.foo message */ +IriSP.TutorialWidget.prototype.fooMessageHandler = function(param) { + + // show that this is preserved correctly. + console.log(this !== window, this); + + this.selector.append(IriSP.templToHTML("
| '+this._get(a,"weekHeader")+" | ":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="=5?' class="ui-datepicker-week-end"':"")+'>'+q[r]+" | "}y+=z+"'+this._get(a,"calculateWeek")(r)+" | ";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&r"+(F&&!D?" ":L?''+ +r.getDate()+"":''+r.getDate()+"")+" | ";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+""}g++;if(g>11){g=0;m++}y+="
|---|
| '+this._get(a,"weekHeader")+" | ":"";for(t=0;t<7;t++){var q=(t+h)%7;A+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+" | "}x+=A+"'+this._get(a,"calculateWeek")(q)+" | ";for(t=0;t<7;t++){var F=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,K=B&&!H||!F[0]||j&&q"+(B&&!w?" ":K?''+q.getDate()+ -"":''+q.getDate()+"")+" | ";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=P+""}g++;if(g>11){g=0;m++}x+="
|---|
+This is a lightly modified version of Kevin Jones' JavaScript +library Data.Dump. To download the original visit: + http://openjsan.org/doc/k/ke/kevinj/Data/Dump/ + +AUTHORS + +The Data.Dump JavaScript module is written by Kevin Jones +(kevinj@cpan.org), based on Data::Dump by Gisle Aas (gisle@aas.no), +based on Data::Dumper by Gurusamy Sarathy (gsar@umich.edu). + +COPYRIGHT + +Copyright 2007 Kevin Jones. Copyright 1998-2000,2003-2004 Gisle Aas. +Copyright 1996-1998 Gurusamy Sarathy. + +This program is free software; you can redistribute it and/or modify +it under the terms of the Perl Artistic License + +See http://www.perl.com/perl/misc/Artistic.html ++ * @static + */ +Dumper = { + /** @param [...] The objects to dump. */ + dump: function () { + if (arguments.length > 1) + return this._dump(arguments); + else if (arguments.length == 1) + return this._dump(arguments[0]); + else + return "()"; + }, + + _dump: function (obj) { + if (typeof obj == 'undefined') return 'undefined'; + var out; + if (obj.serialize) { return obj.serialize(); } + var type = this._typeof(obj); + if (obj.circularReference) obj.circularReference++; + switch (type) { + case 'circular': + out = "{ //circularReference\n}"; + break; + case 'object': + var pairs = new Array; + + for (var prop in obj) { + if (prop != "circularReference" && obj.hasOwnProperty(prop)) { //hide inherited properties + pairs.push(prop + ': ' + this._dump(obj[prop])); + } + } + + out = '{' + this._format_list(pairs) + '}'; + break; + + case 'string': + for (var prop in Dumper.ESC) { + if (Dumper.ESC.hasOwnProperty(prop)) { + obj = obj.replace(prop, Dumper.ESC[prop]); + } + } + + // Escape UTF-8 Strings + if (obj.match(/^[\x00-\x7f]*$/)) { + out = '"' + obj.replace(/\"/g, "\\\"").replace(/([\n\r]+)/g, "\\$1") + '"'; + } + else { + out = "unescape('"+escape(obj)+"')"; + } + break; + + case 'array': + var elems = new Array; + + for (var i=0; i
"; + this.footer = ""; + this.showLinenumbers = true; +} + +JsHilite.cache = {}; + +JsHilite.prototype.hilite = function() { + var hilited = this.tokens.join(""); + var line = 1; + if (this.showLinenumbers) hilited = hilited.replace(/(^|\n)/g, function(m){return m+""+((line<10)? " ":"")+((line<100)? " ":"")+(line++)+" "}); + + return this.header+hilited+this.footer; +} \ No newline at end of file diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/plugins/symbolLink.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/res/jsdoc/app/plugins/symbolLink.js Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,10 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.symbolLink", + { + onSymbolLink: function(link) { + // modify link.linkPath (the href part of the link) + // or link.linkText (the text displayed) + // or link.linkInner (the #name part of the link) + } + } +); \ No newline at end of file diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/plugins/tagParamConfig.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/res/jsdoc/app/plugins/tagParamConfig.js Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,31 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.tagParamConfig", + { + onDocCommentTags: function(comment) { + var currentParam = null; + var tags = comment.tags; + for (var i = 0, l = tags.length; i < l; i++) { + + if (tags[i].title == "param") { + if (tags[i].name.indexOf(".") == -1) { + currentParam = i; + } + } + else if (tags[i].title == "config") { + tags[i].title = "param"; + if (currentParam == null) { + tags[i].name = "arguments"+"."+tags[i].name; + } + else if (tags[i].name.indexOf(tags[currentParam].name+".") != 0) { + tags[i].name = tags[currentParam].name+"."+tags[i].name; + } + currentParam != null + //tags[currentParam].properties.push(tags[i]); + } + else { + currentParam = null; + } + } + } + } +); diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/plugins/tagSynonyms.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/res/jsdoc/app/plugins/tagSynonyms.js Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,43 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.tagSynonyms", + { + onDocCommentSrc: function(comment) { + comment.src = comment.src.replace(/@methodOf\b/i, "@function\n@memberOf"); + comment.src = comment.src.replace(/@fieldOf\b/i, "@field\n@memberOf"); + }, + + onDocCommentTags: function(comment) { + for (var i = 0, l = comment.tags.length; i < l; i++) { + var title = comment.tags[i].title.toLowerCase(); + var syn; + if ((syn = JSDOC.tagSynonyms.synonyms["="+title])) { + comment.tags[i].title = syn; + } + } + } + } +); + +new Namespace( + "JSDOC.tagSynonyms", + function() { + JSDOC.tagSynonyms.synonyms = { + "=member": "memberOf", + "=memberof": "memberOf", + "=description": "desc", + "=exception": "throws", + "=argument": "param", + "=returns": "return", + "=classdescription": "class", + "=fileoverview": "overview", + "=extends": "augments", + "=base": "augments", + "=projectdescription": "overview", + "=classdescription": "class", + "=link": "see", + "=borrows": "inherits", + "=scope": "lends", + "=construct": "constructor" + } + } +); \ No newline at end of file diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/run.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/res/jsdoc/app/run.js Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,348 @@ +/** + * @fileOverview + * A bootstrap script that creates some basic required objects + * for loading other scripts. + * @author Michael Mathews, micmath@gmail.com + * @version $Id: run.js 756 2009-01-07 21:32:58Z micmath $ + */ + +/** + * @namespace Keep track of any messages from the running script. + */ +LOG = { + warn: function(msg, e) { + if (JSDOC.opt.q) return; + if (e) msg = e.fileName+", line "+e.lineNumber+": "+msg; + + msg = ">> WARNING: "+msg; + LOG.warnings.push(msg); + if (LOG.out) LOG.out.write(msg+"\n"); + else print(msg); + }, + + inform: function(msg) { + if (JSDOC.opt.q) return; + msg = " > "+msg; + if (LOG.out) LOG.out.write(msg+"\n"); + else if (typeof LOG.verbose != "undefined" && LOG.verbose) print(msg); + } +}; +LOG.warnings = []; +LOG.verbose = false +LOG.out = undefined; + +/** + * @class Manipulate a filepath. + */ +function FilePath(absPath, separator) { + this.slash = separator || "/"; + this.root = this.slash; + this.path = []; + this.file = ""; + + var parts = absPath.split(/[\\\/]/); + if (parts) { + if (parts.length) this.root = parts.shift() + this.slash; + if (parts.length) this.file = parts.pop() + if (parts.length) this.path = parts; + } + + this.path = this.resolvePath(); +} + +/** Collapse any dot-dot or dot items in a filepath. */ +FilePath.prototype.resolvePath = function() { + var resolvedPath = []; + for (var i = 0; i < this.path.length; i++) { + if (this.path[i] == "..") resolvedPath.pop(); + else if (this.path[i] != ".") resolvedPath.push(this.path[i]); + } + return resolvedPath; +} + +/** Trim off the filename. */ +FilePath.prototype.toDir = function() { + if (this.file) this.file = ""; + return this; +} + +/** Go up a directory. */ +FilePath.prototype.upDir = function() { + this.toDir(); + if (this.path.length) this.path.pop(); + return this; +} + +FilePath.prototype.toString = function() { + return this.root + + this.path.join(this.slash) + + ((this.path.length > 0)? this.slash : "") + + this.file; +} + +/** + * Turn a path into just the name of the file. + */ +FilePath.fileName = function(path) { + var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0); + return path.substring(nameStart); +} + +/** + * Get the extension of a filename + */ +FilePath.fileExtension = function(filename) { + return filename.split(".").pop().toLowerCase(); +}; + +/** + * Turn a path into just the directory part. + */ +FilePath.dir = function(path) { + var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0); + return path.substring(0, nameStart-1); +} + + +importClass(java.lang.System); + +/** + * @namespace A collection of information about your system. + */ +SYS = { + /** + * Information about your operating system: arch, name, version. + * @type string + */ + os: [ + new String(System.getProperty("os.arch")), + new String(System.getProperty("os.name")), + new String(System.getProperty("os.version")) + ].join(", "), + + /** + * Which way does your slash lean. + * @type string + */ + slash: System.getProperty("file.separator")||"/", + + /** + * The path to the working directory where you ran java. + * @type string + */ + userDir: new String(System.getProperty("user.dir")), + + /** + * Where is Java's home folder. + * @type string + */ + javaHome: new String(System.getProperty("java.home")), + + /** + * The absolute path to the directory containing this script. + * @type string + */ + pwd: undefined +}; + +// jsrun appends an argument, with the path to here. +if (arguments[arguments.length-1].match(/^-j=(.+)/)) { + if (RegExp.$1.charAt(0) == SYS.slash || RegExp.$1.charAt(1) == ":") { // absolute path to here + SYS.pwd = new FilePath(RegExp.$1).toDir().toString(); + } + else { // relative path to here + SYS.pwd = new FilePath(SYS.userDir + SYS.slash + RegExp.$1).toDir().toString(); + } + arguments.pop(); +} +else { + print("The run.js script requires you use jsrun.jar."); + quit(); +} + +// shortcut +var File = Packages.java.io.File; + +/** + * @namespace A collection of functions that deal with reading a writing to disk. + */ +IO = { + + /** + * Create a new file in the given directory, with the given name and contents. + */ + saveFile: function(/**string*/ outDir, /**string*/ fileName, /**string*/ content) { + var out = new Packages.java.io.PrintWriter( + new Packages.java.io.OutputStreamWriter( + new Packages.java.io.FileOutputStream(outDir+SYS.slash+fileName), + IO.encoding + ) + ); + out.write(content); + out.flush(); + out.close(); + }, + + /** + * @type string + */ + readFile: function(/**string*/ path) { + if (!IO.exists(path)) { + throw "File doesn't exist there: "+path; + } + return readFile(path, IO.encoding); + }, + + /** + * @param inFile + * @param outDir + * @param [fileName=The original filename] + */ + copyFile: function(/**string*/ inFile, /**string*/ outDir, /**string*/ fileName) { + if (fileName == null) fileName = FilePath.fileName(inFile); + + var inFile = new File(inFile); + var outFile = new File(outDir+SYS.slash+fileName); + + var bis = new Packages.java.io.BufferedInputStream(new Packages.java.io.FileInputStream(inFile), 4096); + var bos = new Packages.java.io.BufferedOutputStream(new Packages.java.io.FileOutputStream(outFile), 4096); + var theChar; + while ((theChar = bis.read()) != -1) { + bos.write(theChar); + } + bos.close(); + bis.close(); + }, + + /** + * Creates a series of nested directories. + */ + mkPath: function(/**Array*/ path) { + if (path.constructor != Array) path = path.split(/[\\\/]/); + var make = ""; + for (var i = 0, l = path.length; i < l; i++) { + make += path[i] + SYS.slash; + if (! IO.exists(make)) { + IO.makeDir(make); + } + } + }, + + /** + * Creates a directory at the given path. + */ + makeDir: function(/**string*/ path) { + (new File(path)).mkdir(); + }, + + /** + * @type string[] + * @param dir The starting directory to look in. + * @param [recurse=1] How many levels deep to scan. + * @returns An array of all the paths to files in the given dir. + */ + ls: function(/**string*/ dir, /**number*/ recurse, _allFiles, _path) { + if (_path === undefined) { // initially + var _allFiles = []; + var _path = [dir]; + } + if (_path.length == 0) return _allFiles; + if (recurse === undefined) recurse = 1; + + dir = new File(dir); + if (!dir.directory) return [String(dir)]; + var files = dir.list(); + + for (var f = 0; f < files.length; f++) { + var file = String(files[f]); + if (file.match(/^\.[^\.\/\\]/)) continue; // skip dot files + + if ((new File(_path.join(SYS.slash)+SYS.slash+file)).list()) { // it's a directory + _path.push(file); + if (_path.length-1 < recurse) IO.ls(_path.join(SYS.slash), recurse, _allFiles, _path); + _path.pop(); + } + else { + _allFiles.push((_path.join(SYS.slash)+SYS.slash+file).replace(SYS.slash+SYS.slash, SYS.slash)); + } + } + + return _allFiles; + }, + + /** + * @type boolean + */ + exists: function(/**string*/ path) { + file = new File(path); + + if (file.isDirectory()){ + return true; + } + if (!file.exists()){ + return false; + } + if (!file.canRead()){ + return false; + } + return true; + }, + + /** + * + */ + open: function(/**string*/ path, /**string*/ append) { + var append = true; + var outFile = new File(path); + var out = new Packages.java.io.PrintWriter( + new Packages.java.io.OutputStreamWriter( + new Packages.java.io.FileOutputStream(outFile, append), + IO.encoding + ) + ); + return out; + }, + + /** + * Sets {@link IO.encoding}. + * Encoding is used when reading and writing text to files, + * and in the meta tags of HTML output. + */ + setEncoding: function(/**string*/ encoding) { + if (/ISO-8859-([0-9]+)/i.test(encoding)) { + IO.encoding = "ISO8859_"+RegExp.$1; + } + else { + IO.encoding = encoding; + } + }, + + /** + * @default "utf-8" + * @private + */ + encoding: "utf-8", + + /** + * Load the given script. + */ + include: function(relativePath) { + load(SYS.pwd+relativePath); + }, + + /** + * Loads all scripts from the given directory path. + */ + includeDir: function(path) { + if (!path) return; + + for (var lib = IO.ls(SYS.pwd+path), i = 0; i < lib.length; i++) + if (/\.js$/i.test(lib[i])) load(lib[i]); + } +} + +// now run the application +IO.include("frame.js"); +IO.include("main.js"); + +main(); diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/t/TestDoc.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/res/jsdoc/app/t/TestDoc.js Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,144 @@ +var TestDoc = { + fails: 0, + plans: 0, + passes: 0, + results: [] +}; + +TestDoc.record = function(result) { + TestDoc.results.push(result); + if (typeof result.verdict == "boolean") { + if (result.verdict === false) TestDoc.fails++; + if (result.verdict === true) TestDoc.passes++; + } +} + +TestDoc.prove = function(filePath) { + if (typeof document != "undefined" && typeof document.write != "undefined") { + if (TestDoc.console) print = function(s) { TestDoc.console.appendChild(document.createTextNode(s+"\n")); } + else print = function(s) { document.write(s+"
@extends tag), but in
+ * reality it is completely unrelated to Shape.
+ * @param {int} sideLength The length of one side for the new Hexagon
+ * @example
+ * var h = new Hexagon(2);
+ * @example
+ * if (hasHex) {
+ * hex = new Hexagon(5);
+ * color = hex.getColor();
+ * }
+ */
+function Hexagon(sideLength) {
+}
+
+
+/**
+ * This is an unattached (static) function that adds two integers together.
+ * @param {int} One The first number to add
+ * @param {int} Two The second number to add
+ * @author Gabriel Reid
+ * @deprecated So you shouldn't use it anymore! Use {@link Shape#getClassName} instead.
+ */
+function Add(One, Two){
+ return One + Two;
+}
+
+
+/**
+ * The color of this shape
+ * @type Color
+ */
+Shape.prototype.color = null;
+
+/**
+ * The border of this shape.
+ * @field
+ * @type int
+ */
+Shape.prototype.border = function(){return border;};
+
+/*
+ * These are all the instance method implementations for Shape
+ */
+
+/**
+ * Get the coordinates of this shape. It is assumed that we're always talking
+ * about shapes in a 2D location here.
+ * @requires The {@link Shape} class
+ * @returns A Coordinate object representing the location of this Shape
+ * @type Coordinate[]
+ */
+Shape.prototype.getCoords = function(){
+ return this.coords;
+}
+
+/**
+ * Get the color of this shape.
+ * @see #setColor
+ * @see The Color library.
+ * @link Shape
+ * @type Color
+ */
+Shape.prototype.getColor = function(){
+ return this.color;
+}
+
+/**
+ * Set the coordinates for this Shape
+ * @param {Coordinate} coordinates The coordinates to set for this Shape
+ */
+Shape.prototype.setCoords = function(coordinates){
+ this.coords = coordinates;
+}
+
+/**
+ * Set the color for this Shape
+ * @param {Color} color The color to set for this Shape
+ * @param other There is no other param, but it can still be documented if
+ * optional parameters are used
+ * @throws NonExistantColorException (no, not really!)
+ * @see #getColor
+ */
+Shape.prototype.setColor = function(color){
+ this.color = color;
+}
+
+/**
+ * Clone this shape
+ * @returns A copy of this shape
+ * @type Shape
+ * @author Gabriel Reid
+ */
+Shape.prototype.clone = function(){
+ return new Shape();
+}
+
+/**
+ * Create a new Rectangle instance.
+ * @class A basic rectangle class, inherits from Shape.
+ * This class could be considered a concrete implementation class
+ * @constructor
+ * @param {int} width The optional width for this Rectangle
+ * @param {int} height Thie optional height for this Rectangle
+ * @author Gabriel Reid
+ * @see Shape is the base class for this
+ * @augments Shape
+ * @hilited
+ */
+function Rectangle(width, // This is the width
+ height // This is the height
+ ){
+ if (width){
+ this.width = width;
+ if (height){
+ this.height = height;
+ }
+ }
+}
+
+
+/* Inherit from Shape */
+Rectangle.prototype = new Shape();
+
+/**
+ * Value to represent the width of the Rectangle.
+ *
+ var x (x < 1);
+ alert("This 'is' \"code\"");
+
+ * @name My Cool Library
+ * @author Joe Smith jsmith@company.com
+ * @version 0.1
+ */
+
+/**
+ * Gets the current foo
+ * @param {String} fooId The unique identifier for the foo.
+ * @return {Object} Returns the current foo.
+ */
+function getFoo(fooID){
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/param_inline.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/param_inline.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,37 @@
+/**
+ @constructor
+ @param columns The number of columns.
+*/
+function Layout(/**int*/columns){
+ /**
+ @param [id] The id of the element.
+ @param elName The name of the element.
+ */
+ this.getElement = function(
+ /** string */ elName,
+ /** number|string */ id
+ ) {
+ };
+
+ /**
+ @constructor
+ */
+ this.Canvas = function(top, left, /**int*/width, height) {
+ /** Is it initiated yet? */
+ this.initiated = true;
+ }
+
+ this.rotate = function(/**nothing*/) {
+ }
+
+ /**
+ @param x
+ @param y
+ @param {zoppler} z*/
+ this.init = function(x, y, /**abbler*/z) {
+ /** The xyz. */
+ this.xyz = x+y+z;
+ this.getXyz = function() {
+ }
+ }
+}
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/params_optional.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/params_optional.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,8 @@
+
+/**
+ * @param {Page[]} pages
+ * @param {number} [id] Specifies the id, if applicable.
+ * @param {String} [title = This is untitled.] Specifies the title.
+ */
+function Document(pages, id, title){
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/prototype.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/prototype.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,17 @@
+/** @constructor */
+function Article() {
+}
+
+Article.prototype.init = function(title) {
+ /** the instance title */
+ this.title = title;
+
+ /** the static counter */
+ Article.counter = 1;
+}
+
+a = new Article();
+a.Init("my title");
+
+print(a.title);
+print(Article.counter);
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/prototype_nested.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/prototype_nested.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,9 @@
+/** @constructor */
+function Word() {
+}
+
+Word.prototype.reverse = function() {
+}
+
+Word.prototype.reverse.utf8 = function() {
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/prototype_oblit.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/prototype_oblit.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,13 @@
+/** @constructor */
+function Article() {
+}
+
+Article.prototype = {
+ /** instance get title */
+ getTitle: function(){
+ }
+}
+
+/** static get title */
+Article.getTitle = function(){
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/prototype_oblit_constructor.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/prototype_oblit_constructor.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,24 @@
+/** @constructor */
+function Article() {
+}
+
+Article.prototype = {
+ /** @constructor */
+ Title: function(title) {
+ /** the value of the Title instance */
+ this.title = title;
+ },
+
+ init: function(pages) {
+ /** the value of the pages of the Article instance */
+ this.pages = pages;
+ }
+}
+
+f = new Article();
+f.init("one two three");
+
+t = new f.Title("my title");
+
+print(f.pages);
+print(t.title);
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/public.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/public.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,10 @@
+/**@constructor*/
+function Foo() {
+ /**
+ @public
+ @static
+ @field
+ */
+ var bar = function(x) {
+ }
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/scripts/code.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/scripts/code.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,5 @@
+/**
+ @class
+ */
+function thisiscode() {
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/scripts/notcode.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/scripts/notcode.txt Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,5 @@
+(This is not code)
+function foo(){{{{
+(
+!
+@
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/shared.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/shared.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,42 @@
+
+/**
+ * Builtin object.
+ * @class
+ * @name Array
+ */
+
+/**#@+
+ * Extension to builtin array.
+ * @memberOf Array
+ * @method
+ */
+
+/**
+ * @returns Boolen if some array members...
+ */
+Array.prototype.some = function(){};
+
+/**
+ * Change every element of an array.
+ * @returns Filtered array copy.
+ */
+Array.prototype.filter = function(){};
+
+/**#@-*/
+
+
+/**
+ * A first in, first out data structure.
+ * @constructor
+ */
+Queue = function(){};
+
+/**#@+
+ * Extension to Queue.
+ * @memberOf Queue
+ */
+
+rewind = function(){
+}
+
+// should close automatically here.
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/shared2.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/shared2.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,2 @@
+startOver = function(){
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/shortcuts.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/shortcuts.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,22 @@
+// /**#=+
+// * {
+// * 'D': 'Date.prototype',
+// * '$N': 'Number'
+// * }
+// */
+// var D = Date.prototype,
+// $N = Number;
+//
+// D.locale = function(){
+// };
+//
+// /**
+// @return {string} The cardinal number string.
+// */
+// $N.nth = function(n){
+// };
+//
+// LOAD.file = function(){
+// }
+//
+// /**#=-*/
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/static_this.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/static_this.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,13 @@
+/** the parent */
+var box = {};
+
+/** @namespace */
+box.holder = {}
+
+box.holder.foo = function() {
+ /** the counter */
+ this.counter = 1;
+}
+
+box.holder.foo();
+print(box.holder.counter);
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/synonyms.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/synonyms.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,31 @@
+/**
+ @class
+ @inherits Bar#zop as #my_zop
+*/
+function Foo() {
+ /** this is a zip. */
+ this.zip = function() {}
+
+ /** from Bar */
+ this.my_zop = new Bar().zop;
+}
+
+/**
+ @class
+ @borrows Foo#zip as this.my_zip
+*/
+function Bar() {
+ /** this is a zop. */
+ this.zop = function() {}
+
+ /** from Foo */
+ this.my_zip = new Foo().zip;
+}
+
+/** @namespace */
+var myObject = {
+ /**
+ @type function
+ */
+ myFunc: getFunction()
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/tosource.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/tosource.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,23 @@
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function valueOf(object) {}
+
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function toString(object) {}
+
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function toSource(object) {}
+
+/**
+ * @param {Object} object
+ * @return {string}
+ */
+function constructor(object) {}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/app/test/variable_redefine.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/app/test/variable_redefine.js Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,14 @@
+/** @constructor */
+function Foo() {
+ var bar = 1;
+ bar = 2; // redefining a private
+
+ this.baz = 1;
+ baz = 2; // global
+
+ /** a private */
+ var blap = {
+ /** in here */
+ tada: 1
+ }
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/changes.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/changes.txt Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,124 @@
+== 2.4.0 ==
+
+ * Fixed bug that added mutiple symbols with the same name to docs.
+ * Added support for the -m option to suppress warnings for multiple docs.
+ * Added patch by brownsea42 to support quoted user variables on the command line. ( issue #281 )
+ * Fixed bug that sometimes caused links to events to be incorrect. ( issue #292 )
+
+== 2.3.3 ==
+
+ * Fixed bug that made all fields declared with the @property tag static. ( issue #262 )
+ * Minor fix to better handle trailing slash on path to template (from jwmetrocat). ( issue #237 )
+ * Fix for @memberOf when applied to inner members. ( issue #264 )
+ * Fix for @memberOf when applied to symbols documented with @name. ( issue #260 )
+ * Applied patch from kunhualqk, fix for bug where @link to borrowed member did not resolve to parent class. ( issue #218 )
+ * Fix for @requires not linking back to the required class
+ * Added experimental support for @constructs to have an argument, the class name, when applied to a function assignment.
+
+== 2.3.2 ==
+
+ * Minor update to the usage notes and corrected the version number displayed in the output.
+
+== 2.3.1 ==
+
+ * Fixed HTML typo in allfiles template. ( issue #228 )
+ * Modified template to display version information for classes.
+ * Modified template to better support multiple methods with the same name.
+ * Fixed bug that caused template to error when backtick characters appeared around class names.
+
+== 2.3.0 ==
+
+ * Added option -u, --unique to avoid bug that causes multiple symbols with names that differ only by case to overwrite each others output on case-insensitive filesystems. ( issue #162 )
+ * Fixed bug where {@links} in @deprecated tags did not resolve. ( issue #220 )
+ * Fixed bug that caused parens around a function to make it to be unrecognized. ( issue #213 )
+ * Fixed bug prevented explicit links to named anchors from working (thanks katgao.pku). ( issue #215 )
+ * Fixed bug that prevented full description from appearing in file overview. ( issue #224 )
+
+== 2.2.1 ==
+
+ * Fixed bug with class template, where sorting of methods was accidentally removed (thanks dezfowler).
+ * Added missing test files for the @exports unit tests.
+
+== 2.2.0 ==
+
+ * Fixed bug that caused exception when given a folder containing non-js files, even with the x commandline option set to "js". ( issue #193 )
+ * Fixed typo in index template [patch submitted by olle]. ( issue #198 )
+ * Modified @borrows tag experimentally to allow for missing "as ..." clause.
+ * Added support for the @exports tag, to allow one symbol to be documented as another.
+ * Added support for the -S option to document code following the Secure Modules pattern.
+
+== 2.1.0 ==
+
+ * Added support for the @event tag.
+ * Fixed bug that prevented the : character from appearing in symbol names.
+ * Fixed bug that prevented underscored symbols marked with @public being tagged as private. (issue #184 )
+ * Fixed bug that randomly affected the @memberOf tag when the name of the symbol did not include the parent name.
+ * Fixed bug that prevented templates that were not in the jsdoc-toolkit folder from being found. ( issue #176 )
+ * Added ability to check for trailing slash on template path. ( issue #177 )
+ * Modified classDesc so that it no longer is appended with the constructor desc.
+ * Fixed call to plugin onDocCommentSrc.
+ * Added missing support for inline doc comments for function return types. ( issue #189 )
+ * Added command line option -q, --quiet.
+ * Added command line option -E, --exclude. ( issue #143 )
+ * Added 2 more hooks for plugins. ( issue #163 )
+ * Added support for extending built-ins. ( issue #160 )
+ * Added "compact" option to JSDOC.JsPlate.prototype.process. ( issue #159 )
+ * @augments no longer documents static members as inherited. ( issue #138 )
+ * @link to a class now goes to the page for that class, not the constructor. ( issue #178 )
+ * Warnings of mismatched curly brace now include filename. ( issue #166 )
+ * Fixed bug affecting template paths loaded via a configuration file when the trailing slash is missing. ( issue #191 )
+ * Minor optimizations.
+
+== 2.0.2 ==
+
+ * Fixed bug that sometimes caused an example of division in the source code to be interpretted as a regex by the JsDoc Toolkit analyzer. ( issue #158 )
+ * Fixed a bug that prevented private variables marked as @public from appearing in the documentation. ( issue #161 )
+ * Fixed bug that prevented variable names with underscored properties from appearing in summaries. ( issue #173 )
+
+== 2.0.1 ==
+
+ * Fixed bug that prevented @fileOverview tag from being recognized.
+ * Added support for @fieldOf as a synonym for @field plus @memberOf.
+ * Added support for @name tag in a @fileOverview comment to control the displayed name of the file.
+ * Added support for multiple @example tags. ( issue #152 )
+ * Modified style sheet of jsdoc template to make more readable. ( issue #151 )
+ * Fixed bug that prevented @since documentation from displaying correctly when it appeared in a class. ( issue #150 )
+ * Fixed bug that caused inhertited properties to sometimes not resolve correctly. ( issue #144 )
+ * Modified so that trailing whitespace in @example is always trimmed. ( issue #153 )
+ * Added support for elseif to JsPlate. (hat tip to fredck)
+ * Added support for @location urls in the @overview comment to the jsdoc template.
+
+== Changes From Versions 1.4.0 to 2.0.0 ==
+
+ * Upgraded included version of Rhino from 1.6 to 1.7R1.
+ * Removed circular references in parsed documentation objects.
+ * Improved inheritance handling, now properties and events can be inherited same as methods.
+ * Improved handling of cross-file relationships, now having two related objects in separate files is not a problem.
+ * Improved ability to recognize membership of previously defined objects.
+ * Added ability to redefine parsing behavior with plugins.
+ * @methodOf is a synonym for @function and @memberOf.
+ * Added @default to document default values of members that are objects.
+ * Added ability to parse and refer to inner functions.
+ * Fixed bug that appeared when calling a method to set properties of the instance referred to by "this".
+ * Added ability to automatically create links to other symbols.
+ * New "jsdoc" template now produces fully W3C valid XHTML.
+ * Inline parameter type hint comments are now documented.
+ * Fixed error: Locally scoped variables (declared with var) no longer appear as global.
+ * It is now possible to run JsDoc Toolkit from any directory.
+ * Added support for inline {@link ...} tags.
+ * Added support for the -H command-line option to allow for custom content handlers.
+ * Tag names @inherits and @scope changed to @borrows and @lends.
+ ? Combining @constructor in a doclet with @lends now supported.
+ * Multiple @lend tags now supported.
+ * Added support for the @constructs tag, used inside a @lends block.
+ * Added support for the @constant tag.
+ * Fixed bug that prevented the use of [] as a default value.
+ * Added support for the @field tag.
+ * Added support for the @public tag (applied to inner functions).
+ * @namespace tag can now be applied to functions, not just object literals.
+ * Added support for the -s command line option to suppress source code output.
+ * Added new unit test framework.
+ * Underscored symbols are now treated as if they have a @private tag by default.
+ * Improved support for anonymous constructors.
+ * Added support for the nocode meta tag.
+
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/conf/sample.conf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/conf/sample.conf Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,31 @@
+/*
+ This is an example of one way you could set up a configuration file to more
+ conveniently define some commandline options. You might like to do this if
+ you frequently reuse the same options. Note that you don't need to define
+ every option in this file, you can combine a configuration file with
+ additional options on the commandline if your wish.
+
+ You would include this configuration file by running JsDoc Toolkit like so:
+ java -jar jsrun.jar app/run.js -c=conf/sample.conf
+
+*/
+
+{
+ // source files to use
+ _: ['app/test/jsdoc_test.js'],
+
+ // document all functions, even uncommented ones
+ a: true,
+
+ // including those marked @private
+ p: true,
+
+ // some extra variables I want to include
+ D: {generatedBy: "Michael Mathews", copyright: "2008"},
+
+ // use this directory as the output directory
+ d: "docs",
+
+ // use this template
+ t: "templates/jsdoc"
+}
\ No newline at end of file
diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/java/build.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sbin/res/jsdoc/java/build.xml Fri Apr 06 16:55:34 2012 +0200
@@ -0,0 +1,36 @@
++ + + + + + +
+ + + + + + + + +| Field Attributes | +Field Name and Description | +
|---|---|
| + |
+
+ Mustache
+
+
+ |
+
| Method Attributes | +Method Name and Description | +
|---|---|
| + |
+ b(a)
+
+
+ |
+
| + |
+ Clusters(json)
+
+
+ |
+
| + |
+ d()
+
+
+ |
+
| + |
+ finished_cb()
+
+
+ |
+
| + |
+ Frames(json)
+
+
+ |
+
| + |
+ handler()
+
+
+ |
+
| + |
+ J(a, c)
+
+
+ |
+
| + |
+ log_error(msg, err)
+
+
+ |
+
| + |
+ log_msg(msg)
+
+
+ |
+
| + |
+ ready_cb()
+
+
+ |
+
1 /* + 2 * + 3 * Copyright 2010 Institut de recherche et d'innovation + 4 * contributor(s) : Samuel Huron + 5 * + 6 * contact@iri.centrepompidou.fr + 7 * http://www.iri.centrepompidou.fr + 8 * + 9 * This software is a computer program whose purpose is to show and add annotations on a video . + 10 * This software is governed by the CeCILL-C license under French law and + 11 * abiding by the rules of distribution of free software. You can use, + 12 * modify and/ or redistribute the software under the terms of the CeCILL-C + 13 * license as circulated by CEA, CNRS and INRIA at the following URL + 14 * "http://www.cecill.info". + 15 * + 16 * The fact that you are presently reading this means that you have had + 17 * knowledge of the CeCILL-C license and that you accept its terms. + 18 */ + 19 /*! LAB.js (LABjs :: Loading And Blocking JavaScript) + 20 v2.0.3 (c) Kyle Simpson + 21 MIT License + 22 */ + 23 + 24 (function(global){ + 25 var _$LAB = global.$LAB, + 26 + 27 // constants for the valid keys of the options object + 28 _UseLocalXHR = "UseLocalXHR", + 29 _AlwaysPreserveOrder = "AlwaysPreserveOrder", + 30 _AllowDuplicates = "AllowDuplicates", + 31 _CacheBust = "CacheBust", + 32 /*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/ + 33 _BasePath = "BasePath", + 34 + 35 // stateless variables used across all $LAB instances + 36 root_page = /^[^?#]*\//.exec(location.href)[0], + 37 root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0], + 38 append_to = document.head || document.getElementsByTagName("head"), + 39 + 40 // inferences... ick, but still necessary + 41 opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style), + 42 + 43 /*!START_DEBUG*/ + 44 // console.log() and console.error() wrappers + 45 log_msg = function(){}, + 46 log_error = log_msg, + 47 /*!END_DEBUG*/ + 48 + 49 // feature sniffs (yay!) + 50 test_script_elem = document.createElement("script"), + 51 explicit_preloading = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29 + 52 real_preloading = explicit_preloading || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized"), // will a script preload with `src` set before DOM append? + 53 script_ordered_async = !real_preloading && test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order + 54 + 55 // XHR preloading (same-domain) and cache-preloading (remote-domain) are the fallbacks (for some browsers) + 56 xhr_or_cache_preloading = !real_preloading && !script_ordered_async && !opera_or_gecko + 57 ; + 58 + 59 /*!START_DEBUG*/ + 60 // define console wrapper functions if applicable + 61 if (global.console && global.console.log) { + 62 if (!global.console.error) global.console.error = global.console.log; + 63 log_msg = function(msg) { global.console.log(msg); }; + 64 log_error = function(msg,err) { global.console.error(msg,err); }; + 65 } + 66 /*!END_DEBUG*/ + 67 + 68 // test for function + 69 function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; } + 70 + 71 // test for array + 72 function is_array(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; } + 73 + 74 // make script URL absolute/canonical + 75 function canonical_uri(src,base_path) { + 76 var absolute_regex = /^\w+\:\/\//; + 77 + 78 // is `src` is protocol-relative (begins with // or ///), prepend protocol + 79 if (/^\/\/\/?/.test(src)) { + 80 src = location.protocol + src; + 81 } + 82 // is `src` page-relative? (not an absolute URL, and not a domain-relative path, beginning with /) + 83 else if (!absolute_regex.test(src) && src.charAt(0) != "/") { + 84 // prepend `base_path`, if any + 85 src = (base_path || "") + src; + 86 } + 87 // make sure to return `src` as absolute + 88 return absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src); + 89 } + 90 + 91 // merge `source` into `target` + 92 function merge_objs(source,target) { + 93 for (var k in source) { if (source.hasOwnProperty(k)) { + 94 target[k] = source[k]; // TODO: does this need to be recursive for our purposes? + 95 }} + 96 return target; + 97 } + 98 + 99 // does the chain group have any ready-to-execute scripts? +100 function check_chain_group_scripts_ready(chain_group) { +101 var any_scripts_ready = false; +102 for (var i=0; i<chain_group.scripts.length; i++) { +103 if (chain_group.scripts[i].ready && chain_group.scripts[i].exec_trigger) { +104 any_scripts_ready = true; +105 chain_group.scripts[i].exec_trigger(); +106 chain_group.scripts[i].exec_trigger = null; +107 } +108 } +109 return any_scripts_ready; +110 } +111 +112 // creates a script load listener +113 function create_script_load_listener(elem,registry_item,flag,onload) { +114 elem.onload = elem.onreadystatechange = function() { +115 if ((elem.readyState && elem.readyState != "complete" && elem.readyState != "loaded") || registry_item[flag]) return; +116 elem.onload = elem.onreadystatechange = null; +117 onload(); +118 }; +119 } +120 +121 // script executed handler +122 function script_executed(registry_item) { +123 registry_item.ready = registry_item.finished = true; +124 for (var i=0; i<registry_item.finished_listeners.length; i++) { +125 registry_item.finished_listeners[i](); +126 } +127 registry_item.ready_listeners = []; +128 registry_item.finished_listeners = []; +129 } +130 +131 // make the request for a scriptha +132 function request_script(chain_opts,script_obj,registry_item,onload,preload_this_script) { +133 // setTimeout() "yielding" prevents some weird race/crash conditions in older browsers +134 setTimeout(function(){ +135 var script, src = script_obj.real_src, xhr; +136 +137 // don't proceed until `append_to` is ready to append to +138 if ("item" in append_to) { // check if `append_to` ref is still a live node list +139 if (!append_to[0]) { // `append_to` node not yet ready +140 // try again in a little bit -- note: will re-call the anonymous function in the outer setTimeout, not the parent `request_script()` +141 setTimeout(arguments.callee,25); +142 return; +143 } +144 // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists +145 append_to = append_to[0]; +146 } +147 script = document.createElement("script"); +148 if (script_obj.type) script.type = script_obj.type; +149 if (script_obj.charset) script.charset = script_obj.charset; +150 +151 // should preloading be used for this script? +152 if (preload_this_script) { +153 // real script preloading? +154 if (real_preloading) { +155 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload: "+src);/*!END_DEBUG*/ +156 registry_item.elem = script; +157 if (explicit_preloading) { // explicit preloading (aka, Zakas' proposal) +158 script.preload = true; +159 script.onpreload = onload; +160 } +161 else { +162 script.onreadystatechange = function(){ +163 if (script.readyState == "loaded") onload(); +164 }; +165 } +166 script.src = src; +167 // NOTE: no append to DOM yet, appending will happen when ready to execute +168 } +169 // same-domain and XHR allowed? use XHR preloading +170 else if (preload_this_script && src.indexOf(root_domain) == 0 && chain_opts[_UseLocalXHR]) { +171 xhr = new XMLHttpRequest(); // note: IE never uses XHR (it supports true preloading), so no more need for ActiveXObject fallback for IE <= 7 +172 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (xhr): "+src);/*!END_DEBUG*/ +173 xhr.onreadystatechange = function() { +174 if (xhr.readyState == 4) { +175 xhr.onreadystatechange = function(){}; // fix a memory leak in IE +176 registry_item.text = xhr.responseText + "\n//@ sourceURL=" + src; // http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/ +177 onload(); +178 } +179 }; +180 xhr.open("GET",src); +181 xhr.send(); +182 } +183 // as a last resort, use cache-preloading +184 else { +185 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (cache): "+src);/*!END_DEBUG*/ +186 script.type = "text/cache-script"; +187 create_script_load_listener(script,registry_item,"ready",function() { +188 append_to.removeChild(script); +189 onload(); +190 }); +191 script.src = src; +192 append_to.insertBefore(script,append_to.firstChild); +193 } +194 } +195 // use async=false for ordered async? parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order +196 else if (script_ordered_async) { +197 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load (ordered async): "+src);/*!END_DEBUG*/ +198 script.async = false; +199 create_script_load_listener(script,registry_item,"finished",onload); +200 script.src = src; +201 append_to.insertBefore(script,append_to.firstChild); +202 } +203 // otherwise, just a normal script element +204 else { +205 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load: "+src);/*!END_DEBUG*/ +206 create_script_load_listener(script,registry_item,"finished",onload); +207 script.src = src; +208 append_to.insertBefore(script,append_to.firstChild); +209 } +210 },0); +211 } +212 +213 // create a clean instance of $LAB +214 function create_sandbox() { +215 var global_defaults = {}, +216 can_use_preloading = real_preloading || xhr_or_cache_preloading, +217 queue = [], +218 registry = {}, +219 instanceAPI +220 ; +221 +222 // global defaults +223 global_defaults[_UseLocalXHR] = true; +224 global_defaults[_AlwaysPreserveOrder] = false; +225 global_defaults[_AllowDuplicates] = false; +226 global_defaults[_CacheBust] = false; +227 /*!START_DEBUG*/global_defaults[_Debug] = false;/*!END_DEBUG*/ +228 global_defaults[_BasePath] = ""; +229 +230 // execute a script that has been preloaded already +231 function execute_preloaded_script(chain_opts,script_obj,registry_item) { +232 var script; +233 +234 function preload_execute_finished() { +235 if (script != null) { // make sure this only ever fires once +236 script = null; +237 script_executed(registry_item); +238 } +239 } +240 +241 if (registry[script_obj.src].finished) return; +242 if (!chain_opts[_AllowDuplicates]) registry[script_obj.src].finished = true; +243 +244 script = registry_item.elem || document.createElement("script"); +245 if (script_obj.type) script.type = script_obj.type; +246 if (script_obj.charset) script.charset = script_obj.charset; +247 create_script_load_listener(script,registry_item,"finished",preload_execute_finished); +248 +249 // script elem was real-preloaded +250 if (registry_item.elem) { +251 registry_item.elem = null; +252 } +253 // script was XHR preloaded +254 else if (registry_item.text) { +255 script.onload = script.onreadystatechange = null; // script injection doesn't fire these events +256 script.text = registry_item.text; +257 } +258 // script was cache-preloaded +259 else { +260 script.src = script_obj.real_src; +261 } +262 append_to.insertBefore(script,append_to.firstChild); +263 +264 // manually fire execution callback for injected scripts, since events don't fire +265 if (registry_item.text) { +266 preload_execute_finished(); +267 } +268 } +269 +270 // process the script request setup +271 function do_script(chain_opts,script_obj,chain_group,preload_this_script) { +272 var registry_item, +273 registry_items, +274 ready_cb = function(){ script_obj.ready_cb(script_obj,function(){ execute_preloaded_script(chain_opts,script_obj,registry_item); }); }, +275 finished_cb = function(){ script_obj.finished_cb(script_obj,chain_group); } +276 ; +277 +278 script_obj.src = canonical_uri(script_obj.src,chain_opts[_BasePath]); +279 script_obj.real_src = script_obj.src + +280 // append cache-bust param to URL? +281 (chain_opts[_CacheBust] ? ((/\?.*$/.test(script_obj.src) ? "&_" : "?_") + ~~(Math.random()*1E9) + "=") : "") +282 ; +283 +284 if (!registry[script_obj.src]) registry[script_obj.src] = {items:[],finished:false}; +285 registry_items = registry[script_obj.src].items; +286 +287 // allowing duplicates, or is this the first recorded load of this script? +288 if (chain_opts[_AllowDuplicates] || registry_items.length == 0) { +289 registry_item = registry_items[registry_items.length] = { +290 ready:false, +291 finished:false, +292 ready_listeners:[ready_cb], +293 finished_listeners:[finished_cb] +294 }; +295 +296 request_script(chain_opts,script_obj,registry_item, +297 // which callback type to pass? +298 ( +299 (preload_this_script) ? // depends on script-preloading +300 function(){ +301 registry_item.ready = true; +302 for (var i=0; i<registry_item.ready_listeners.length; i++) { +303 registry_item.ready_listeners[i](); +304 } +305 registry_item.ready_listeners = []; +306 } : +307 function(){ script_executed(registry_item); } +308 ), +309 // signal if script-preloading should be used or not +310 preload_this_script +311 ); +312 } +313 else { +314 registry_item = registry_items[0]; +315 if (registry_item.finished) { +316 finished_cb(); +317 } +318 else { +319 registry_item.finished_listeners.push(finished_cb); +320 } +321 } +322 } +323 +324 // creates a closure for each separate chain spawned from this $LAB instance, to keep state cleanly separated between chains +325 function create_chain() { +326 var chainedAPI, +327 chain_opts = merge_objs(global_defaults,{}), +328 chain = [], +329 exec_cursor = 0, +330 scripts_currently_loading = false, +331 group +332 ; +333 +334 // called when a script has finished preloading +335 function chain_script_ready(script_obj,exec_trigger) { +336 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script preload finished: "+script_obj.real_src);/*!END_DEBUG*/ +337 script_obj.ready = true; +338 script_obj.exec_trigger = exec_trigger; +339 advance_exec_cursor(); // will only check for 'ready' scripts to be executed +340 } +341 +342 // called when a script has finished executing +343 function chain_script_executed(script_obj,chain_group) { +344 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script execution finished: "+script_obj.real_src);/*!END_DEBUG*/ +345 script_obj.ready = script_obj.finished = true; +346 script_obj.exec_trigger = null; +347 // check if chain group is all finished +348 for (var i=0; i<chain_group.scripts.length; i++) { +349 if (!chain_group.scripts[i].finished) return; +350 } +351 // chain_group is all finished if we get this far +352 chain_group.finished = true; +353 advance_exec_cursor(); +354 } +355 +356 // main driver for executing each part of the chain +357 function advance_exec_cursor() { +358 while (exec_cursor < chain.length) { +359 if (is_func(chain[exec_cursor])) { +360 /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);/*!END_DEBUG*/ +361 try { chain[exec_cursor++](); } catch (err) { +362 /*!START_DEBUG*/if (chain_opts[_Debug]) log_error("$LAB.wait() error caught: ",err);/*!END_DEBUG*/ +363 } +364 continue; +365 } +366 else if (!chain[exec_cursor].finished) { +367 if (check_chain_group_scripts_ready(chain[exec_cursor])) continue; +368 break; +369 } +370 exec_cursor++; +371 } +372 // we've reached the end of the chain (so far) +373 if (exec_cursor == chain.length) { +374 scripts_currently_loading = false; +375 group = false; +376 } +377 } +378 +379 // setup next chain script group +380 function init_script_chain_group() { +381 if (!group || !group.scripts) { +382 chain.push(group = {scripts:[],finished:true}); +383 } +384 } +385 +386 // API for $LAB chains +387 chainedAPI = { +388 // start loading one or more scripts +389 script:function(){ +390 for (var i=0; i<arguments.length; i++) { +391 (function(script_obj,script_list){ +392 var splice_args; +393 +394 if (!is_array(script_obj)) { +395 script_list = [script_obj]; +396 } +397 for (var j=0; j<script_list.length; j++) { +398 init_script_chain_group(); +399 script_obj = script_list[j]; +400 +401 if (is_func(script_obj)) script_obj = script_obj(); +402 if (!script_obj) continue; +403 if (is_array(script_obj)) { +404 // set up an array of arguments to pass to splice() +405 splice_args = [].slice.call(script_obj); // first include the actual array elements we want to splice in +406 splice_args.unshift(j,1); // next, put the `index` and `howMany` parameters onto the beginning of the splice-arguments array +407 [].splice.apply(script_list,splice_args); // use the splice-arguments array as arguments for splice() +408 j--; // adjust `j` to account for the loop's subsequent `j++`, so that the next loop iteration uses the same `j` index value +409 continue; +410 } +411 if (typeof script_obj == "string") script_obj = {src:script_obj}; +412 script_obj = merge_objs(script_obj,{ +413 ready:false, +414 ready_cb:chain_script_ready, +415 finished:false, +416 finished_cb:chain_script_executed +417 }); +418 group.finished = false; +419 group.scripts.push(script_obj); +420 +421 do_script(chain_opts,script_obj,group,(can_use_preloading && scripts_currently_loading)); +422 scripts_currently_loading = true; +423 +424 if (chain_opts[_AlwaysPreserveOrder]) chainedAPI.wait(); +425 } +426 })(arguments[i],arguments[i]); +427 } +428 return chainedAPI; +429 }, +430 // force LABjs to pause in execution at this point in the chain, until the execution thus far finishes, before proceeding +431 wait:function(){ +432 if (arguments.length > 0) { +433 for (var i=0; i<arguments.length; i++) { +434 chain.push(arguments[i]); +435 } +436 group = chain[chain.length-1]; +437 } +438 else group = false; +439 +440 advance_exec_cursor(); +441 +442 return chainedAPI; +443 } +444 }; +445 +446 // the first chain link API (includes `setOptions` only this first time) +447 return { +448 script:chainedAPI.script, +449 wait:chainedAPI.wait, +450 setOptions:function(opts){ +451 merge_objs(opts,chain_opts); +452 return chainedAPI; +453 } +454 }; +455 } +456 +457 // API for each initial $LAB instance (before chaining starts) +458 instanceAPI = { +459 // main API functions +460 setGlobalDefaults:function(opts){ +461 merge_objs(opts,global_defaults); +462 return instanceAPI; +463 }, +464 setOptions:function(){ +465 return create_chain().setOptions.apply(null,arguments); +466 }, +467 script:function(){ +468 return create_chain().script.apply(null,arguments); +469 }, +470 wait:function(){ +471 return create_chain().wait.apply(null,arguments); +472 }, +473 +474 // built-in queuing for $LAB `script()` and `wait()` calls +475 // useful for building up a chain programmatically across various script locations, and simulating +476 // execution of the chain +477 queueScript:function(){ +478 queue[queue.length] = {type:"script", args:[].slice.call(arguments)}; +479 return instanceAPI; +480 }, +481 queueWait:function(){ +482 queue[queue.length] = {type:"wait", args:[].slice.call(arguments)}; +483 return instanceAPI; +484 }, +485 runQueue:function(){ +486 var $L = instanceAPI, len=queue.length, i=len, val; +487 for (;--i>=0;) { +488 val = queue.shift(); +489 $L = $L[val.type].apply(null,val.args); +490 } +491 return $L; +492 }, +493 +494 // rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB +495 noConflict:function(){ +496 global.$LAB = _$LAB; +497 return instanceAPI; +498 }, +499 +500 // create another clean instance of $LAB +501 sandbox:function(){ +502 return create_sandbox(); +503 } +504 }; +505 +506 return instanceAPI; +507 } +508 +509 // create the main instance of $LAB +510 global.$LAB = create_sandbox(); +511 +512 +513 /* The following "hack" was suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html +514 NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?). +515 +516 The hack essentially "patches" the **page** that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does +517 proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked +518 document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. +519 For instance, jQuery 1.4+ has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or +520 fixed by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs. +521 */ +522 (function(addEvent,domLoaded,handler){ +523 if (document.readyState == null && document[addEvent]){ +524 document.readyState = "loading"; +525 document[addEvent](domLoaded,handler = function(){ +526 document.removeEventListener(domLoaded,handler,false); +527 document.readyState = "complete"; +528 },false); +529 } +530 })("addEventListener","DOMContentLoaded"); +531 +532 })(this);/* +533 mustache.js — Logic-less templates in JavaScript +534 +535 See http://mustache.github.com/ for more info. +536 */ +537 +538 var Mustache = function() { +539 var Renderer = function() {}; +540 +541 Renderer.prototype = { +542 otag: "{{", +543 ctag: "}}", +544 pragmas: {}, +545 buffer: [], +546 pragmas_implemented: { +547 "IMPLICIT-ITERATOR": true +548 }, +549 context: {}, +550 +551 render: function(template, context, partials, in_recursion) { +552 // reset buffer & set context +553 if(!in_recursion) { +554 this.context = context; +555 this.buffer = []; // TODO: make this non-lazy +556 } +557 +558 // fail fast +559 if(!this.includes("", template)) { +560 if(in_recursion) { +561 return template; +562 } else { +563 this.send(template); +564 return; +565 } +566 } +567 +568 template = this.render_pragmas(template); +569 var html = this.render_section(template, context, partials); +570 if(in_recursion) { +571 return this.render_tags(html, context, partials, in_recursion); +572 } +573 +574 this.render_tags(html, context, partials, in_recursion); +575 }, +576 +577 /* +578 Sends parsed lines +579 */ +580 send: function(line) { +581 if(line !== "") { +582 this.buffer.push(line); +583 } +584 }, +585 +586 /* +587 Looks for %PRAGMAS +588 */ +589 render_pragmas: function(template) { +590 // no pragmas +591 if(!this.includes("%", template)) { +592 return template; +593 } +594 +595 var that = this; +596 var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + +597 this.ctag, "g"); +598 return template.replace(regex, function(match, pragma, options) { +599 if(!that.pragmas_implemented[pragma]) { +600 throw({message: +601 "This implementation of mustache doesn't understand the '" + +602 pragma + "' pragma"}); +603 } +604 that.pragmas[pragma] = {}; +605 if(options) { +606 var opts = options.split("="); +607 that.pragmas[pragma][opts[0]] = opts[1]; +608 } +609 return ""; +610 // ignore unknown pragmas silently +611 }); +612 }, +613 +614 /* +615 Tries to find a partial in the curent scope and render it +616 */ +617 render_partial: function(name, context, partials) { +618 name = this.trim(name); +619 if(!partials || partials[name] === undefined) { +620 throw({message: "unknown_partial '" + name + "'"}); +621 } +622 if(typeof(context[name]) != "object") { +623 return this.render(partials[name], context, partials, true); +624 } +625 return this.render(partials[name], context[name], partials, true); +626 }, +627 +628 /* +629 Renders inverted (^) and normal (#) sections +630 */ +631 render_section: function(template, context, partials) { +632 if(!this.includes("#", template) && !this.includes("^", template)) { +633 return template; +634 } +635 +636 var that = this; +637 // CSW - Added "+?" so it finds the tighest bound, not the widest +638 var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag + +639 "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag + +640 "\\s*", "mg"); +641 +642 // for each {{#foo}}{{/foo}} section do... +643 return template.replace(regex, function(match, type, name, content) { +644 var value = that.find(name, context); +645 if(type == "^") { // inverted section +646 if(!value || that.is_array(value) && value.length === 0) { +647 // false or empty list, render it +648 return that.render(content, context, partials, true); +649 } else { +650 return ""; +651 } +652 } else if(type == "#") { // normal section +653 if(that.is_array(value)) { // Enumerable, Let's loop! +654 return that.map(value, function(row) { +655 return that.render(content, that.create_context(row), +656 partials, true); +657 }).join(""); +658 } else if(that.is_object(value)) { // Object, Use it as subcontext! +659 return that.render(content, that.create_context(value), +660 partials, true); +661 } else if(typeof value === "function") { +662 // higher order section +663 return value.call(context, content, function(text) { +664 return that.render(text, context, partials, true); +665 }); +666 } else if(value) { // boolean section +667 return that.render(content, context, partials, true); +668 } else { +669 return ""; +670 } +671 } +672 }); +673 }, +674 +675 /* +676 Replace {{foo}} and friends with values from our view +677 */ +678 render_tags: function(template, context, partials, in_recursion) { +679 // tit for tat +680 var that = this; +681 +682 var new_regex = function() { +683 return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + +684 that.ctag + "+", "g"); +685 }; +686 +687 var regex = new_regex(); +688 var tag_replace_callback = function(match, operator, name) { +689 switch(operator) { +690 case "!": // ignore comments +691 return ""; +692 case "=": // set new delimiters, rebuild the replace regexp +693 that.set_delimiters(name); +694 regex = new_regex(); +695 return ""; +696 case ">": // render partial +697 return that.render_partial(name, context, partials); +698 case "{": // the triple mustache is unescaped +699 return that.find(name, context); +700 default: // escape the value +701 return that.escape(that.find(name, context)); +702 } +703 }; +704 var lines = template.split("\n"); +705 for(var i = 0; i < lines.length; i++) { +706 lines[i] = lines[i].replace(regex, tag_replace_callback, this); +707 if(!in_recursion) { +708 this.send(lines[i]); +709 } +710 } +711 +712 if(in_recursion) { +713 return lines.join("\n"); +714 } +715 }, +716 +717 set_delimiters: function(delimiters) { +718 var dels = delimiters.split(" "); +719 this.otag = this.escape_regex(dels[0]); +720 this.ctag = this.escape_regex(dels[1]); +721 }, +722 +723 escape_regex: function(text) { +724 // thank you Simon Willison +725 if(!arguments.callee.sRE) { +726 var specials = [ +727 '/', '.', '*', '+', '?', '|', +728 '(', ')', '[', ']', '{', '}', '\\' +729 ]; +730 arguments.callee.sRE = new RegExp( +731 '(\\' + specials.join('|\\') + ')', 'g' +732 ); +733 } +734 return text.replace(arguments.callee.sRE, '\\$1'); +735 }, +736 +737 /* +738 find `name` in current `context`. That is find me a value +739 from the view object +740 */ +741 find: function(name, context) { +742 name = this.trim(name); +743 +744 // Checks whether a value is thruthy or false or 0 +745 function is_kinda_truthy(bool) { +746 return bool === false || bool === 0 || bool; +747 } +748 +749 var value; +750 if(is_kinda_truthy(context[name])) { +751 value = context[name]; +752 } else if(is_kinda_truthy(this.context[name])) { +753 value = this.context[name]; +754 } +755 +756 if(typeof value === "function") { +757 return value.apply(context); +758 } +759 if(value !== undefined) { +760 return value; +761 } +762 // silently ignore unkown variables +763 return ""; +764 }, +765 +766 // Utility methods +767 +768 /* includes tag */ +769 includes: function(needle, haystack) { +770 return haystack.indexOf(this.otag + needle) != -1; +771 }, +772 +773 /* +774 Does away with nasty characters +775 */ +776 escape: function(s) { +777 s = String(s === null ? "" : s); +778 return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) { +779 switch(s) { +780 case "&": return "&"; +781 case "\\": return "\\\\"; +782 case '"': return '"'; +783 case "'": return '''; +784 case "<": return "<"; +785 case ">": return ">"; +786 default: return s; +787 } +788 }); +789 }, +790 +791 // by @langalex, support for arrays of strings +792 create_context: function(_context) { +793 if(this.is_object(_context)) { +794 return _context; +795 } else { +796 var iterator = "."; +797 if(this.pragmas["IMPLICIT-ITERATOR"]) { +798 iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; +799 } +800 var ctx = {}; +801 ctx[iterator] = _context; +802 return ctx; +803 } +804 }, +805 +806 is_object: function(a) { +807 return a && typeof a == "object"; +808 }, +809 +810 is_array: function(a) { +811 return Object.prototype.toString.call(a) === '[object Array]'; +812 }, +813 +814 /* +815 Gets rid of leading and trailing whitespace +816 */ +817 trim: function(s) { +818 return s.replace(/^\s*|\s*$/g, ""); +819 }, +820 +821 /* +822 Why, why, why? Because IE. Cry, cry cry. +823 */ +824 map: function(array, fn) { +825 if (typeof array.map == "function") { +826 return array.map(fn); +827 } else { +828 var r = []; +829 var l = array.length; +830 for(var i = 0; i < l; i++) { +831 r.push(fn(array[i])); +832 } +833 return r; +834 } +835 } +836 }; +837 +838 return({ +839 name: "mustache.js", +840 version: "0.3.1-dev", +841 +842 /* +843 Turns a template and view into HTML +844 */ +845 to_html: function(template, view, partials, send_fun) { +846 var renderer = new Renderer(); +847 if(send_fun) { +848 renderer.send = send_fun; +849 } +850 renderer.render(template, view, partials); +851 if(!send_fun) { +852 return renderer.buffer.join("\n"); +853 } +854 } +855 }); +856 }(); +857 // Underscore.js 1.2.3 +858 // (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc. +859 // Underscore is freely distributable under the MIT license. +860 // Portions of Underscore are inspired or borrowed from Prototype, +861 // Oliver Steele's Functional, and John Resig's Micro-Templating. +862 // For all details and documentation: +863 // http://documentcloud.github.com/underscore +864 (function(){function r(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== +865 c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&r(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(m.call(a,h)&&(f++,!(g=m.call(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(m.call(c, +866 h)&&!f--)break;g=!f}}d.pop();return g}var s=this,F=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,G=k.concat,H=k.unshift,l=p.toString,m=p.hasOwnProperty,v=k.forEach,w=k.map,x=k.reduce,y=k.reduceRight,z=k.filter,A=k.every,B=k.some,q=k.indexOf,C=k.lastIndexOf,p=Array.isArray,I=Object.keys,t=Function.prototype.bind,b=function(a){return new n(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else typeof define==="function"&& +867 define.amd?define("underscore",function(){return b}):s._=b;b.VERSION="1.2.3";var j=b.each=b.forEach=function(a,c,b){if(a!=null)if(v&&a.forEach===v)a.forEach(c,b);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(b,a[e],e,a)===o)break}else for(e in a)if(m.call(a,e)&&c.call(b,a[e],e,a)===o)break};b.map=function(a,c,b){var e=[];if(a==null)return e;if(w&&a.map===w)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});return e};b.reduce=b.foldl=b.inject=function(a, +868 c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(x&&a.reduce===x)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(y&&a.reduceRight===y)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g, +869 c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,c,b){var e;D(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.filter===z)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(A&&a.every===A)return a.every(c, +870 b);j(a,function(a,g,h){if(!(e=e&&c.call(b,a,g,h)))return o});return e};var D=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(B&&a.some===B)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return q&&a.indexOf===q?a.indexOf(c)!=-1:b=D(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a, +871 d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a, +872 computed:b})});return e.value};b.shuffle=function(a){var c=[],b;j(a,function(a,f){f==0?c[0]=a:(b=Math.floor(Math.random()*(f+1)),c[f]=c[b],c[b]=a)});return c};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,c){var b=a.criteria,d=c.criteria;return b<d?-1:b>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex= +873 function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length- +874 1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d}, +875 []);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a, +876 c,d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(q&&a.indexOf===q)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(C&&a.lastIndexOf===C)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g}; +877 var E=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));E.prototype=a.prototype;var b=new E,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a, +878 c){var d={};c||(c=b.identity);return function(){var b=c.apply(this,arguments);return m.call(d,b)?d[b]:d[b]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true: +879 a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=G.apply([a],arguments);return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after= +880 function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=I||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)m.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){j(i.call(arguments, +881 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(m.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a=== +882 Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!m.call(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)== +883 "[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){s._=F;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),function(c){J(c, +884 b[c]=a[c])})};var K=0;b.uniqueId=function(a){var b=K++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape,function(a,b){return"',_.escape("+b.replace(/\\'/g,"'")+"),'"}).replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g, +885 "'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};var n=function(a){this._wrapped=a};b.prototype=n.prototype;var u=function(a,c){return c?b(a).chain():a},J=function(a,c){n.prototype[a]=function(){var a=i.call(arguments);H.call(a,this._wrapped);return u(c.apply(b, +886 a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];n.prototype[a]=function(){b.apply(this._wrapped,arguments);return u(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];n.prototype[a]=function(){return u(b.apply(this._wrapped,arguments),this._chain)}});n.prototype.chain=function(){this._chain=true;return this};n.prototype.value=function(){return this._wrapped}}).call(this); +887 /* main file */ +888 +889 if ( window.IriSP === undefined && window.__IriSP === undefined ) { +890 var IriSP = {}; +891 var __IriSP = IriSP; /* for backward compatibility */ +892 } +893 +894 IriSP.loadLibs = function( libs, config, metadata_url, callback ) { +895 // Localize jQuery variable +896 IriSP.jQuery = null; +897 var $L = $LAB.script(libs.jQuery).script(libs.swfObject) +898 .script(libs.jQueryUI) +899 +900 if (config.player.type === "jwplayer") { +901 // load our popcorn.js lookalike +902 $L = $L.script(libs.jwplayer); +903 } else { +904 // load the real popcorn +905 $L = $L.script(libs.popcorn).script(libs["popcorn.code"]); +906 if (config.player.type === "youtube") { +907 $L = $L.script(libs["popcorn.youtube"]); +908 } +909 if (config.player.type === "vimeo") +910 $L = $L.script(libs["popcorn.vimeo"]); +911 +912 /* do nothing for html5 */ +913 } +914 +915 /* widget specific requirements */ +916 for (var idx in config.gui.widgets) { +917 if (config.gui.widgets[idx].type === "PolemicWidget") { +918 $L.script(libs.raphael); +919 } +920 } +921 +922 // same for modules +923 /* +924 for (var idx in config.modules) { +925 if (config.modules[idx].type === "PolemicWidget") +926 $L.script(libs.raphaelJs); +927 } +928 */ +929 +930 $L.wait(function() { +931 IriSP.jQuery = window.jQuery.noConflict( true ); +932 IriSP._ = window._.noConflict(); +933 IriSP.underscore = IriSP._; +934 +935 var css_link_jquery = IriSP.jQuery( "<link>", { +936 rel: "stylesheet", +937 type: "text/css", +938 href: libs.cssjQueryUI, +939 'class': "dynamic_css" +940 } ); +941 var css_link_custom = IriSP.jQuery( "<link>", { +942 rel: "stylesheet", +943 type: "text/css", +944 href: config.gui.css, +945 'class': "dynamic_css" +946 } ); +947 +948 css_link_jquery.appendTo('head'); +949 css_link_custom.appendTo('head'); +950 +951 IriSP.setupDataLoader(); +952 IriSP.__dataloader.get(metadata_url, +953 function(data) { +954 /* save the data so that we could re-use it to +955 configure the video +956 */ +957 IriSP.__jsonMetadata = data; +958 callback.call(window) }); +959 }); +960 }; +961 IriSP.annotation_template = "{{! template for an annotation displayed in a segmentWidget }}<div title='{{divTitle}}' id='{{id}}' class='Ldt-iri-chapter' style='left: {{startPixel}}px; width: {{pxWidth}}px; background-color:#{{hexa_color}};' ></div>"; +962 IriSP.annotationWidget_template = "{{! template for the annotation widget }}<div class='Ldt-AnnotationsWidget'> <!-- ugly div because we want to have a double border --> <div class='Ldt-Annotation-DoubleBorder'> <div class='Ldt-AnnotationContent'> <div class='Ldt-AnnotationShareIcons'> <a class='Ldt-fbShare' href=''><img src='{{img_dir}}/facebook.png' alt='share on facebook'></img></a> <a class='Ldt-TwShare' href=''><img src='{{img_dir}}/twitter.png' alt='share on twitter'></img></a> <a class='Ldt-GplusShare' href=''><img src='{{img_dir}}/google.png' alt='share on google+'></img></a> </div> <div class='Ldt-SaTitle'></div> <div class='Ldt-SaDescription'></div> </div> </div></div>"; +963 IriSP.annotation_loading_template = "{{! template shown while the annotation widget is loading }}<div id='Ldt-load-container'><div id='Ldt-loader'> </div> Chargement... </div>"; +964 IriSP.arrowWidget_template = "<div class='Ldt-arrowWidget'></div>"; +965 IriSP.overlay_marker_template = "{{! the template for the small bars which is z-indexed over our segment widget }}<div class='Ldt-SegmentPositionMarker' style='background-color: #F7268E;'></div>"; +966 IriSP.player_template = "{{! template for the radio player }}<div class='Ldt-controler demo'> <div class='Ldt-LeftPlayerControls'> <div class='Ldt-button Ldt-CtrlPlay'></div> <div class='Ldt-button Ldt-CtrlAnnotate'></div> <div class='Ldt-button Ldt-CtrlSearch'></div> </div> <div class='Ldt-RightPlayerControls'> <div class='Ldt-Time'> <div class='Ldt-ElapsedTime'></div> <div class='Ldt-TimeSeparator'>/</div> <div class='Ldt-TotalTime'></div> </div> <div class='Ldt-button Ldt-CtrlSound'></div> </div></div>"; +967 IriSP.search_template = "{{! template for the search container }}<div class='LdtSearchContainer' style='margin-left: {{margin_left}}; position: absolute; margin-top: -60px;'> <div class='LdtSearch' style='display: none; background-color: #EEE; width: 165px; boder: 1px; border-color: #CFCFCF; position: absolute; text-align: center;'> <input class='LdtSearchInput' style='margin-top: 2px; margin-bottom: 2px;' /> </div></div><div class='cleaner'></div>"; +968 IriSP.share_template = "{{! social network sharing template }}<a onclick='__IriSP.MyApiPlayer.share(\'delicious\');' title='partager avec delicious'><span class='share shareDelicious'> </span></a> <a onclick='__IriSP.MyApiPlayer.share(\'facebook\');' title='partager avec facebook'> <span class='share shareFacebook'> </span></a><a onclick='__IriSP.MyApiPlayer.share(\'twitter\');' title='partager avec twitter'> <span class='share shareTwitter'> </span></a><a onclick='__IriSP.MyApiPlayer.share(\'myspace\');' title='partager avec Myspace'> <span class='share shareMySpace'> </span></a>"; +969 IriSP.sliderWidget_template = "{{! template for the slider widget - it's composed of two divs we one overlayed on top of the other }}<div class='Ldt-sliderBackground'></div><div class='Ldt-sliderForeground'></div><div class='Ldt-sliderPositionMarker'></div>"; +970 IriSP.tooltip_template = "{{! template used by the jquery ui tooltip }}<div class='Ldt-tooltip'> <div class='title'>{{title}}</div> <div class='time'>{{begin}} : {{end}} </div> <div class='description'>{{description}}</div></div>"; +971 IriSP.tooltipWidget_template = "{{! template for the tooltip widget }}<div class='tip'> <div class='tipcolor' style='height:10px;width:10px'></div> <div class='tiptext'></div>"; +972 IriSP.tweetWidget_template = "{{! template for the tweet widget }}<div class='Ldt-tweetWidget'> <div class='Ldt-tweet-DoubleBorder'> <img src='{{img_dir}}/minimize.png' class='Ldt-tweetWidgetKeepOpen' alt='dont minimize automatically'></img> <img src='{{img_dir}}/minimize.png' class='Ldt-tweetWidgetMinimize' alt='minimize window'></img> <div class='Ldt-tweetAvatar'></div> <img src='{{img_dir}}/profile_arrow.png' class='Ldt-tweetAvatar-profileArrow'></img> <div class='Ldt-tweetContents'></div> <a href='' target='_blank' class='Ldt-Retweet'><div class='Ldt-RetweetIcon'></div> - Retweet </a> <a href='' target='_blank' class='Ldt-TweetReply'><div class='Ldt-TweetReplyIcon'></div> - Reply</a> </div></div>";/* wrapper that simulates popcorn.js because +973 popcorn is a bit unstable at the time */ +974 +975 IriSP.PopcornReplacement = { +976 msgPump : {} /* used by jquery to receive and send messages */ +977 }; +978 +979 IriSP.PopcornReplacement.media = { +980 "paused": true, +981 "muted": false +982 }; +983 +984 IriSP.PopcornReplacement.listen = function(msg, callback) { +985 // IriSP.jQuery(IriSP.PopcornReplacement.msgPump).bind(msg, function(event, rest) { callback(rest); }); +986 if (!IriSP.PopcornReplacement.msgPump.hasOwnProperty(msg)) +987 IriSP.PopcornReplacement.msgPump[msg] = []; +988 +989 IriSP.PopcornReplacement.msgPump[msg].push(callback); +990 }; +991 +992 IriSP.PopcornReplacement.trigger = function(msg, params) { +993 // IriSP.jQuery(IriSP.PopcornReplacement.msgPump).trigger(msg, params); +994 +995 if (!IriSP.PopcornReplacement.msgPump.hasOwnProperty(msg)) +996 return; +997 +998 var d = IriSP.PopcornReplacement.msgPump[msg]; +999 for(var entry in d) { +1000 d[entry].call(window, params); +1001 } +1002 +1003 }; +1004 +1005 IriSP.PopcornReplacement.guid = function(prefix) { +1006 var str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { +1007 var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); +1008 return v.toString(16); +1009 }); +1010 +1011 return prefix + str; +1012 }; +1013 +1014 IriSP.PopcornReplacement.__initApi = function() { +1015 IriSP.PopcornReplacement.trigger("loadedmetadata"); // we've done more than loading metadata of course, +1016 // but popcorn doesn't need to know more. +1017 IriSP.PopcornReplacement.media.muted = jwplayer(IriSP.PopcornReplacement._container).getMute(); +1018 }; +1019 +1020 IriSP.PopcornReplacement.jwplayer = function(container, options) { +1021 IriSP.PopcornReplacement._container = container.slice(1); //eschew the '#' +1022 options.events = { +1023 onReady: IriSP.PopcornReplacement.__initApi, +1024 onTime: IriSP.PopcornReplacement.__timeHandler, +1025 onPlay: IriSP.PopcornReplacement.__playHandler, +1026 onPause: IriSP.PopcornReplacement.__pauseHandler, +1027 onSeek: IriSP.PopcornReplacement.__seekHandler +1028 } +1029 +1030 jwplayer(IriSP.PopcornReplacement._container).setup(options); +1031 IriSP.PopcornReplacement.media.duration = options.duration; +1032 return IriSP.PopcornReplacement; +1033 }; +1034 +1035 IriSP.PopcornReplacement.currentTime = function(time) { +1036 if (typeof(time) === "undefined") { +1037 return jwplayer(IriSP.PopcornReplacement._container).getPosition(); +1038 } else { +1039 var currentTime = +time; +1040 jwplayer( IriSP.PopcornReplacement._container ).seek( currentTime ); +1041 IriSP.PopcornReplacement.trigger("seeked"); +1042 return jwplayer(IriSP.PopcornReplacement._container).getPosition(); +1043 } +1044 }; +1045 +1046 IriSP.PopcornReplacement.play = function() { +1047 IriSP.PopcornReplacement.media.paused = false; +1048 IriSP.PopcornReplacement.trigger("play"); +1049 // IriSP.PopcornReplacement.trigger("playing"); +1050 jwplayer( IriSP.PopcornReplacement._container ).play(); +1051 }; +1052 +1053 IriSP.PopcornReplacement.pause = function() { +1054 if ( !IriSP.PopcornReplacement.media.paused ) { +1055 IriSP.PopcornReplacement.media.paused = true; +1056 IriSP.PopcornReplacement.trigger( "pause" ); +1057 jwplayer( IriSP.PopcornReplacement._container ).pause(); +1058 } +1059 }; +1060 +1061 IriSP.PopcornReplacement.muted = function(val) { +1062 if (typeof(val) !== "undefined") { +1063 +1064 if (jwplayer(IriSP.PopcornReplacement._container).getMute() !== val) { +1065 if (val) { +1066 jwplayer(IriSP.PopcornReplacement._container).setMute(true); +1067 IriSP.PopcornReplacement.media.muted = true; +1068 } else { +1069 jwplayer( IriSP.PopcornReplacement._container ).setMute(false); +1070 IriSP.PopcornReplacement.media.muted = false; +1071 } +1072 +1073 IriSP.PopcornReplacement.trigger( "volumechange" ); +1074 } +1075 +1076 return jwplayer( IriSP.PopcornReplacement._container ).getMute(); +1077 } else { +1078 return jwplayer( IriSP.PopcornReplacement._container ).getMute(); +1079 } +1080 }; +1081 +1082 IriSP.PopcornReplacement.mute = IriSP.PopcornReplacement.muted; +1083 +1084 IriSP.PopcornReplacement.__codes = []; +1085 IriSP.PopcornReplacement.code = function(options) { +1086 IriSP.PopcornReplacement.__codes.push(options); +1087 return IriSP.PopcornReplacement; +1088 }; +1089 +1090 IriSP.PopcornReplacement.__runCode = function() { +1091 var currentTime = jwplayer(IriSP.PopcornReplacement._container).getPosition(); +1092 var i = 0; +1093 for(i = 0; i < IriSP.PopcornReplacement.__codes.length; i++) { +1094 var c = IriSP.PopcornReplacement.__codes[i]; +1095 if (currentTime == c.start) { +1096 c.onStart(); +1097 } +1098 +1099 if (currentTime == c.end) { +1100 c.onEnd(); +1101 } +1102 +1103 } +1104 }; +1105 +1106 /* called everytime the player updates itself +1107 (onTime event) +1108 */ +1109 +1110 IriSP.PopcornReplacement.__timeHandler = function(event) { +1111 var pos = event.position; +1112 +1113 var i = 0; +1114 for(i = 0; i < IriSP.PopcornReplacement.__codes.length; i++) { +1115 var c = IriSP.PopcornReplacement.__codes[i]; +1116 +1117 if (pos >= c.start && pos < c.end && +1118 pos - 0.1 <= c.start) { +1119 c.onStart(); +1120 } +1121 +1122 if (pos > c.start && pos > c.end && +1123 pos - 0.1 <= c.end) { +1124 console.log("eonedn"); +1125 c.onEnd(); +1126 } +1127 +1128 } +1129 +1130 IriSP.PopcornReplacement.trigger("timeupdate"); +1131 }; +1132 +1133 IriSP.PopcornReplacement.__seekHandler = function(event) { +1134 var i = 0; +1135 +1136 for(i = 0; i < IriSP.PopcornReplacement.__codes.length; i++) { +1137 var c = IriSP.PopcornReplacement.__codes[i]; +1138 +1139 if (event.position >= c.start && event.position < c.end) { +1140 c.onEnd(); +1141 } +1142 } +1143 +1144 for(i = 0; i < IriSP.PopcornReplacement.__codes.length; i++) { +1145 var c = IriSP.PopcornReplacement.__codes[i]; +1146 +1147 if (typeof(event.offset) === "undefined") +1148 event.offset = 0; +1149 +1150 if (event.offset >= c.start && event.offset < c.end) { +1151 c.onStart(); +1152 } +1153 +1154 } +1155 +1156 IriSP.PopcornReplacement.trigger("timeupdate"); +1157 }; +1158 +1159 +1160 IriSP.PopcornReplacement.__playHandler = function(event) { +1161 IriSP.PopcornReplacement.media.paused = false; +1162 IriSP.PopcornReplacement.trigger("play"); +1163 }; +1164 +1165 IriSP.PopcornReplacement.__pauseHandler = function(event) { +1166 IriSP.PopcornReplacement.media.paused = true; +1167 IriSP.PopcornReplacement.trigger("pause"); +1168 }; +1169 +1170 IriSP.PopcornReplacement.roundTime = function() { +1171 var currentTime = IriSP.PopcornReplacement.currentTime(); +1172 return Math.round(currentTime); +1173 }; +1174 /* utils.js - various utils that don't belong anywhere else */ +1175 +1176 /* trace function, for debugging */ +1177 +1178 IriSP.traceNum = 0; +1179 IriSP.trace = function( msg, value ) { +1180 /* +1181 if( IriSP.config.gui.debug === true ) { +1182 IriSP.traceNum += 1; +1183 IriSP.jQuery( "<div>"+IriSP.traceNum+" - "+msg+" : "+value+"</div>" ).appendTo( "#Ldt-output" ); +1184 } +1185 */ +1186 }; +1187 +1188 /* used in callbacks - because in callbacks we lose "this", +1189 we need to have a special function which wraps "this" in +1190 a closure. This way, the +1191 */ +1192 IriSP.wrap = function (obj, fn) { +1193 return function() { +1194 var args = Array.prototype.slice.call(arguments, 0); +1195 return fn.apply(obj, args); +1196 } +1197 } +1198 +1199 /* convert a time to a percentage in the media */ +1200 IriSP.timeToPourcent = function(time, timetotal){ +1201 var time = Math.abs(time); +1202 var timetotal = Math.abs(timetotal); +1203 +1204 return Math.floor((time/timetotal) * 100); +1205 }; +1206 +1207 IriSP.padWithZeros = function(num) { +1208 if (Math.abs(num) < 10) { +1209 return "0" + num.toString(); +1210 } else { +1211 return num.toString(); +1212 } +1213 }; +1214 /* convert a number of seconds to a tuple of the form +1215 [hours, minutes, seconds] +1216 */ +1217 IriSP.secondsToTime = function(secs) { +1218 var hours = Math.abs(parseInt( secs / 3600 ) % 24); +1219 var minutes = Math.abs(parseInt( secs / 60 ) % 60); +1220 var seconds = parseFloat(Math.abs(secs % 60).toFixed(0)); +1221 +1222 var toString_fn = function() { +1223 var ret = ""; +1224 if (hours > 0) +1225 ret = IriSP.padWithZeros(this.hours) + ":"; +1226 ret += IriSP.padWithZeros(this.minutes) + ":" + IriSP.padWithZeros(this.seconds); +1227 +1228 return ret; +1229 } +1230 return {"hours" : hours, "minutes" : minutes, "seconds" : seconds, toString: toString_fn}; +1231 }; +1232 +1233 IriSP.secondsToString +1234 +1235 /* format a tweet - replaces @name by a link to the profile, #hashtag, etc. */ +1236 IriSP.formatTweet = function(tweet) { +1237 /* +1238 an array of arrays which hold a regexp and its replacement. +1239 */ +1240 var regExps = [ +1241 /* copied from http://codegolf.stackexchange.com/questions/464/shortest-url-regex-match-in-javascript/480#480 */ +1242 [/((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)/gi, "<a href='$1'>$1</a>"], +1243 [/@(\w+)/gi, "<a href='http://twitter.com/$1'>@$1</a>"], // matches a @handle +1244 [/#(\w+)/gi, "<a href='http://twitter.com/search?q=%23$1'>#$1</a>"],// matches a hashtag +1245 [/(\+\+)/gi, "<span class='Ldt-PolemicPlusPlus'>$1</span>"], +1246 [/(--)/gi, "<span class='Ldt-PolemicMinusMinus'>$1</span>"], +1247 [/(==)/gi, "<span class='Ldt-PolemicEqualEqual'>$1</span>"], +1248 [/(\?\?)/gi, "<span class='Ldt-PolemicQuestion'>$1</span>"] +1249 ]; +1250 +1251 var i = 0; +1252 for(i = 0; i < regExps.length; i++) { +1253 tweet = tweet.replace(regExps[i][0], regExps[i][1]); +1254 } +1255 +1256 return tweet; +1257 }; +1258 +1259 IriSP.countProperties = function(obj) { +1260 var count = 0; +1261 +1262 for(var prop in obj) { +1263 if(obj.hasOwnProperty(prop)) +1264 ++count; +1265 } +1266 +1267 return count; +1268 }; +1269 +1270 // conversion de couleur Decimal vers HexaDecimal || 000 si fff +1271 IriSP.DEC_HEXA_COLOR = function (dec) { +1272 var hexa='0123456789ABCDEF'; +1273 var hex=''; +1274 var tmp; +1275 while (dec>15){ +1276 tmp = dec-(Math.floor(dec/16))*16; +1277 hex = hexa.charAt(tmp)+hex; +1278 dec = Math.floor(dec/16); +1279 } +1280 hex = hexa.charAt(dec)+hex; +1281 return(hex); +1282 }; +1283 +1284 /* shortcut to have global variables in templates */ +1285 IriSP.templToHTML = function(template, values) { +1286 var params = IriSP.jQuery.extend(IriSP.default_templates_vars, values); +1287 return Mustache.to_html(template, params); +1288 }; +1289 +1290 /* we need to be stricter than encodeURIComponent, +1291 because of twitter +1292 */ +1293 IriSP.encodeURI = function(str) { +1294 return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28'). +1295 replace(/\)/g, '%29').replace(/\*/g, '%2A'); +1296 } +1297 +1298 IriSP.__guidCounter = 0; +1299 IriSP.guid = function(prefix) { +1300 IriSP.__guidCounter += 1; +1301 return prefix + IriSP.__guidCounter; +1302 }; +1303 /* for ie compatibility +1304 if (Object.prototype.__defineGetter__&&!Object.defineProperty) { +1305 Object.defineProperty=function(obj,prop,desc) { +1306 if ("get" in desc) obj.__defineGetter__(prop,desc.get); +1307 if ("set" in desc) obj.__defineSetter__(prop,desc.set); +1308 } +1309 } +1310 */ +1311 /* data.js - this file deals with how the players gets and sends data */ +1312 +1313 IriSP.DataLoader = function() { +1314 this._cache = {}; +1315 +1316 /* +1317 A structure to hold callbacks for specific urls. We need it because +1318 ajax calls are asynchronous, so it means that sometimes we ask +1319 multiple times for a ressource because the first call hasn't been +1320 received yet. +1321 */ +1322 this._callbacks = {}; +1323 }; +1324 +1325 IriSP.DataLoader.prototype.get = function(url, callback) { +1326 +1327 var base_url = url.split("&")[0] +1328 if (this._cache.hasOwnProperty(base_url)) { +1329 callback(this._cache[base_url]); +1330 } else { +1331 if (!this._callbacks.hasOwnProperty(base_url)) { +1332 this._callbacks[base_url] = []; +1333 this._callbacks[base_url].push(callback); +1334 /* we need a closure because this gets lost when it's called back */ +1335 +1336 // uncomment you don't want to use caching. +1337 // IriSP.jQuery.get(url, callback); +1338 +1339 var func = function(data) { +1340 this._cache[base_url] = data; +1341 var i = 0; +1342 +1343 for (i = 0; i < this._callbacks[base_url].length; i++) { +1344 this._callbacks[base_url][i](this._cache[base_url]); +1345 } +1346 }; +1347 +1348 /* automagically choose between json and jsonp */ +1349 if (url.indexOf(document.location.hostname) === -1 && +1350 url.indexOf("http://") !== -1 /* not a relative url */ ) { +1351 // we contacting a foreign domain, use JSONP +1352 +1353 IriSP.jQuery.get(url, {}, IriSP.wrap(this, func), "jsonp"); +1354 } else { +1355 +1356 // otherwise, hey, whatever rows your boat +1357 IriSP.jQuery.get(url, IriSP.wrap(this, func)); +1358 } +1359 +1360 } else { +1361 /* simply push the callback - it'll get called when the ressource +1362 has been received */ +1363 +1364 this._callbacks[base_url].push(callback); +1365 +1366 } +1367 } +1368 } +1369 +1370 /* the base abstract "class" */ +1371 IriSP.Serializer = function(DataLoader, url) { +1372 this._DataLoader = DataLoader; +1373 this._url = url; +1374 this._data = []; +1375 }; +1376 +1377 IriSP.Serializer.prototype.serialize = function(data) { }; +1378 IriSP.Serializer.prototype.deserialize = function(data) {}; +1379 +1380 IriSP.Serializer.prototype.currentMedia = function() { +1381 }; +1382 +1383 IriSP.Serializer.prototype.sync = function(callback) { +1384 callback.call(this, this._data); +1385 }; +1386 +1387 IriSP.SerializerFactory = function(DataLoader) { +1388 this._dataloader = DataLoader; +1389 }; +1390 +1391 IriSP.SerializerFactory.prototype.getSerializer = function(metadataOptions) { +1392 /* This function returns serializer set-up with the correct +1393 configuration - takes a metadata struct describing the metadata source +1394 */ +1395 +1396 if (metadataOptions === undefined) +1397 /* return an empty serializer */ +1398 return IriSP.Serializer("", ""); +1399 +1400 switch(metadataOptions.type) { +1401 case "json": +1402 return new IriSP.JSONSerializer(this._dataloader, metadataOptions.src); +1403 break; +1404 +1405 case "dummy": /* only used for unit testing - not defined in production */ +1406 return new IriSP.MockSerializer(this._dataloader, metadataOptions.src); +1407 break; +1408 +1409 case "empty": +1410 return new IriSP.Serializer("", "empty"); +1411 break; +1412 +1413 default: +1414 return undefined; +1415 } +1416 }; +1417 /* site.js - all our site-dependent config : player chrome, cdn locations, etc...*/ +1418 +1419 IriSP.lib = { +1420 jQuery : "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js", +1421 jQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/jquery-ui.js", +1422 jQueryToolTip : "http://cdn.jquerytools.org/1.2.4/all/jquery.tools.min.js", +1423 swfObject : "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js", +1424 cssjQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/base/jquery-ui.css", +1425 popcorn : "/mdp/src/js/libs/popcorn.js", +1426 jwplayer : "/mdp/src/js/libs/jwplayer.js", +1427 popcornReplacement: "/mdp/src/js/libs/pop.js", +1428 raphael: "/mdp/src/js/libs/raphael.js", +1429 "popcorn.mediafragment" : "/mdp/src/js/libs/popcorn.mediafragment.js", +1430 "popcorn.code" : "/mdp/src/js/libs/popcorn.code.js", +1431 "popcorn.jwplayer": "/mdp/src/js/libs/popcorn.jwplayer.js", +1432 "popcorn.youtube": "/mdp/src/js/libs/popcorn.youtube.js" +1433 }; +1434 +1435 //Player Configuration +1436 IriSP.config = undefined; +1437 +1438 IriSP.widgetsDefaults = { +1439 "LayoutManager" : {spacer_div_height : "0px" }, +1440 "PlayerWidget" : {}, +1441 "AnnotationsWidget": { +1442 "share_text" : "I'm watching ", +1443 "fb_link" : "http://www.facebook.com/share.php?u=", +1444 "tw_link" : "http://twitter.com/home?status=", +1445 "gplus_link" : "" +1446 }, +1447 "TweetsWidget" : { +1448 default_profile_picture : "https://si0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png", +1449 tweet_display_period: 10000 // how long do we show a tweet ? +1450 }, +1451 "SliderWidget" : { +1452 minimize_period: 850 // how long does the slider stays maximized after the user leaves the zone ? +1453 }, +1454 "Main" : { +1455 autoplay: true +1456 } +1457 +1458 }; +1459 +1460 IriSP.paths = { +1461 // "imgs": "/tweetlive/res/metadataplayer/src/css/imgs" +1462 "imgs": "/mdp/src/css/imgs" +1463 }; +1464 IriSP.default_templates_vars = { +1465 "img_dir" : IriSP.paths.imgs +1466 }; +1467 +1468 /* ui.js - ui related functions */ +1469 +1470 /* FIXME: use an sharing library */ +1471 IriSP.LdtShareTool = IriSP.share_template; /* the contents come from share.html */ +1472 +1473 IriSP.createPlayerChrome = function(){ +1474 var width = IriSP.config.gui.width; +1475 var height = IriSP.config.gui.height; +1476 var heightS = IriSP.config.gui.height-20; +1477 +1478 // AUDIO */ +1479 // PB dans le html : ; +1480 IriSP.trace( "__IriSP.createMyHtml",IriSP.config.gui.container ); +1481 +1482 +1483 /* FIXME : factor this in another file */ +1484 if( IriSP.config.gui.mode=="radio" ){ +1485 +1486 IriSP.jQuery( "#"+IriSP.config.gui.container ).before(IriSP.search_template); +1487 var radioPlayer = Mustache.to_html(IriSP.radio_template, {"share_template" : IriSP.share_template}); +1488 IriSP.jQuery(radioPlayer).appendTo("#"+IriSP.config.gui.container); +1489 +1490 // special tricks for IE 7 +1491 if (IriSP.jQuery.browser.msie==true && IriSP.jQuery.browser.version=="7.0"){ +1492 //LdtSearchContainer +1493 //__IriSP.jQuery("#LdtPlayer").attr("margin-top","50px"); +1494 IriSP.jQuery("#Ldt-Root").css("padding-top","25px"); +1495 IriSP.trace("__IriSP.createHtml","IE7 SPECIAL "); +1496 } +1497 } else if(IriSP.config.gui.mode=="video") { +1498 +1499 var videoPlayer = Mustache.to_html(IriSP.video_template, {"share_template" : IriSP.share_template, "heightS" : heightS}); +1500 IriSP.jQuery(videoPlayer).appendTo("#"+IriSP.config.gui.container); +1501 } +1502 +1503 IriSP.jQuery("#Ldt-Annotations").width(width-(75*2)); +1504 IriSP.jQuery("#Ldt-Show-Arrow-container").width(width-(75*2)); +1505 IriSP.jQuery("#Ldt-ShowAnnotation-audio").width(width-10); +1506 IriSP.jQuery("#Ldt-ShowAnnotation-video").width(width-10); +1507 IriSP.jQuery("#Ldt-SaKeyword").width(width-10); +1508 IriSP.jQuery("#Ldt-controler").width(width-10); +1509 IriSP.jQuery("#Ldt-Control").attr("z-index","100"); +1510 IriSP.jQuery("#Ldt-controler").hide(); +1511 +1512 IriSP.jQuery(IriSP.annotation_loading_template).appendTo("#Ldt-ShowAnnotation-audio"); +1513 +1514 if(IriSP.config.gui.mode=='radio'){ +1515 IriSP.jQuery("#Ldt-load-container").attr("width",IriSP.config.gui.width); +1516 } +1517 // Show or not the output +1518 if(IriSP.config.gui.debug===true){ +1519 IriSP.jQuery("#Ldt-output").show(); +1520 } else { +1521 IriSP.jQuery("#Ldt-output").hide(); +1522 } +1523 +1524 }; +1525 +1526 +1527 /* create the buttons and the slider */ +1528 IriSP.createInterface = function( width, height, duration ) { +1529 +1530 IriSP.jQuery( "#Ldt-controler" ).show(); +1531 //__IriSP.jQuery("#Ldt-Root").css('display','visible'); +1532 IriSP.trace( "__IriSP.createInterface" , width+","+height+","+duration+"," ); +1533 +1534 IriSP.jQuery( "#Ldt-ShowAnnotation").click( function () { +1535 //__IriSP.jQuery(this).slideUp(); +1536 } ); +1537 +1538 var LdtpPlayerY = IriSP.jQuery("#Ldt-PlaceHolder").attr("top"); +1539 var LdtpPlayerX = IriSP.jQuery("#Ldt-PlaceHolder").attr("left"); +1540 +1541 IriSP.jQuery( "#slider-range-min" ).slider( { //range: "min", +1542 value: 0, +1543 min: 1, +1544 max: duration/1000,//1:54:52.66 = 3600+3240+ +1545 step: 0.1, +1546 slide: function(event, ui) { +1547 +1548 //__IriSP.jQuery("#amount").val(ui.value+" s"); +1549 //player.sendEvent('SEEK', ui.value) +1550 IriSP.MyApiPlayer.seek(ui.value); +1551 //changePageUrlOffset(ui.value); +1552 //player.sendEvent('PAUSE') +1553 } +1554 } ); +1555 +1556 IriSP.trace("__IriSP.createInterface","ICI"); +1557 IriSP.jQuery("#amount").val(IriSP.jQuery("#slider-range-min").slider("value")+" s"); +1558 IriSP.jQuery(".Ldt-Control1 button:first").button({ +1559 icons: { +1560 primary: 'ui-icon-play' +1561 }, +1562 text: false +1563 }).next().button({ +1564 icons: { +1565 primary: 'ui-icon-seek-next' +1566 }, +1567 text: false +1568 }); +1569 IriSP.jQuery(".Ldt-Control2 button:first").button({ +1570 icons: { +1571 primary: 'ui-icon-search'//, +1572 //secondary: 'ui-icon-volume-off' +1573 }, +1574 text: false +1575 }).next().button({ +1576 icons: { +1577 primary: 'ui-icon-volume-on' +1578 }, +1579 text: false +1580 }); +1581 +1582 // /!\ PB A MODIFIER +1583 //__IriSP.MyTags.draw(); +1584 IriSP.trace("__IriSP.createInterface","ICI2"); +1585 IriSP.jQuery( "#ldt-CtrlPlay" ).attr( "style", "background-color:#CD21C24;" ); +1586 +1587 IriSP.jQuery( "#Ldt-load-container" ).hide(); +1588 +1589 if( IriSP.config.gui.mode=="radio" & IriSP.jQuery.browser.msie != true ) { +1590 IriSP.jQuery( "#Ldtplayer1" ).attr( "height", "0" ); +1591 } +1592 IriSP.trace( "__IriSP.createInterface" , "3" ); +1593 +1594 IriSP.trace( "__IriSP.createInterface", "END" ); +1595 +1596 }; +1597 /* the widget classes and definitions */ +1598 +1599 IriSP.Widget = function(Popcorn, config, Serializer) { +1600 +1601 if (config === undefined || config === null) { +1602 config = {} +1603 } +1604 +1605 this._Popcorn = Popcorn; +1606 this._config = config; +1607 this._serializer = Serializer; +1608 +1609 if (config.hasOwnProperty("container")) { +1610 this._id = config.container; +1611 this.selector = IriSP.jQuery("#" + this._id); +1612 } +1613 +1614 if (config.hasOwnProperty("spacer")) { +1615 this._spacerId = config.spacer; +1616 this.spacer = IriSP.jQuery("#" + this._spacerId); +1617 } +1618 +1619 +1620 if (config.hasOwnProperty("width")) { +1621 // this.width and not this._width because we consider it public. +1622 this.width = config.width; +1623 } +1624 +1625 if (config.hasOwnProperty("height")) { +1626 this.height = config.height; +1627 } +1628 +1629 if (config.hasOwnProperty("heightmax")) { +1630 this.heightmax = config.heightmax; +1631 } +1632 +1633 if (config.hasOwnProperty("widthmax")) { +1634 this.widthmax = config.widthmax; +1635 } +1636 +1637 }; +1638 +1639 IriSP.Widget.prototype.draw = function() { +1640 /* implemented by "sub-classes" */ +1641 }; +1642 +1643 IriSP.Widget.prototype.redraw = function() { +1644 /* implemented by "sub-classes" */ +1645 }; +1646 /* modules are non-graphical entities, similar to widgets */ +1647 +1648 IriSP.Module = function(Popcorn, config, Serializer) { +1649 +1650 if (config === undefined || config === null) { +1651 config = {} +1652 } +1653 +1654 this._Popcorn = Popcorn; +1655 this._config = config; +1656 this._serializer = Serializer; +1657 }; +1658 /* layout.js - very basic layout management */ +1659 +1660 /* +1661 a layout manager manages a div and the layout of objects +1662 inside it. +1663 */ +1664 +1665 IriSP.LayoutManager = function(options) { +1666 this._Popcorn = null; +1667 this._widgets = []; +1668 +1669 this._div = "LdtPlayer"; +1670 this._width = 640; +1671 +1672 if (options === undefined) { +1673 options = {}; +1674 }; +1675 +1676 if (options.hasOwnProperty('container')) { +1677 this._div = options.container; +1678 } +1679 +1680 if (options.hasOwnProperty('width')) { +1681 this._width = options.width; +1682 } +1683 +1684 if (options.hasOwnProperty('height')) { +1685 this._height = options.height; +1686 } +1687 +1688 /* this is a shortcut */ +1689 this.selector = IriSP.jQuery("#" + this._div); +1690 +1691 this.selector.css("width", this._width); +1692 +1693 if (this._height !== undefined) +1694 this.selector.css("height", this._height); +1695 }; +1696 +1697 /* we need this special setter because of a chicken and egg problem : +1698 we want the manager to use popcorn but the popcorn div will be managed +1699 by the manager. So we need a way to set the instance the manager uses +1700 */ +1701 +1702 IriSP.LayoutManager.prototype.setPopcornInstance = function(popcorn) { +1703 this._Popcorn = popcorn; +1704 } +1705 +1706 /* stem is a string to append to the id of the widget */ +1707 IriSP.LayoutManager.prototype.createDiv = function(stem) { +1708 if (typeof(stem) === "undefined") +1709 stem = ""; +1710 +1711 var newDiv = IriSP.guid(this._div + "_widget_" + stem + "_"); +1712 var spacerDiv = IriSP.guid("LdtPlayer_spacer_"); +1713 this._widgets.push(newDiv); +1714 +1715 var divTempl = "<div id='{{id}}' style='width: {{width}}px; position: relative;'></div"; +1716 var spacerTempl = "<div id='{{spacer_id}}' style='width: {{width}}px; position: relative; height: {{spacer_div_height}};'></div"; +1717 +1718 var divCode = Mustache.to_html(divTempl, {id: newDiv, width: this._width}); +1719 var spacerCode = Mustache.to_html(spacerTempl, {spacer_id: spacerDiv, width: this._width, +1720 spacer_div_height: IriSP.widgetsDefaults.LayoutManager.spacer_div_height }); +1721 +1722 this.selector.append(divCode); +1723 this.selector.append(spacerCode); +1724 +1725 return [newDiv, spacerDiv]; +1726 }; +1727 /* init.js - initialization and configuration of Popcorn and the widgets +1728 exemple json configuration: +1729 +1730 */ +1731 +1732 IriSP.setupDataLoader = function() { +1733 /* we set it up separately because we need to +1734 get data at the very beginning, for instance when +1735 setting up the video */ +1736 IriSP.__dataloader = new IriSP.DataLoader(); +1737 }; +1738 +1739 IriSP.configurePopcorn = function (layoutManager, options) { +1740 var pop; +1741 var ret = layoutManager.createDiv(); +1742 var containerDiv = ret[0]; +1743 +1744 switch(options.type) { +1745 /* +1746 todo : dynamically create the div/video tag which +1747 will contain the video. +1748 */ +1749 case "html5": +1750 var tmpId = Popcorn.guid("video"); +1751 IriSP.jQuery("#" + containerDiv).append("<video src='" + options.file + "' id='" + tmpId + "'></video>"); +1752 +1753 if (options.hasOwnProperty("width")) +1754 IriSP.jQuery("#" + containerDiv).css("width", options.width); +1755 +1756 if (options.hasOwnProperty("height")) +1757 IriSP.jQuery("#" + containerDiv).css("height", options.height); +1758 +1759 pop = Popcorn("#" + tmpId); +1760 break; +1761 +1762 case "jwplayer": +1763 var opts = IriSP.jQuery.extend({}, options); +1764 delete opts.container; +1765 +1766 if (options.provider === "rtmp") { +1767 /* exit if we can't access the metadata */ +1768 if (typeof(IriSP.__jsonMetadata) === "undefined") { +1769 break; +1770 }; +1771 +1772 +1773 // the json format is totally illogical +1774 opts.streamer = IriSP.__jsonMetadata["medias"][0]["meta"]["item"]["value"]; +1775 var source = IriSP.__jsonMetadata["medias"][0]["href"]; +1776 +1777 // the source if a full url but jwplayer wants an url relative to the +1778 // streamer url, so we've got to remove the common part. +1779 opts.file = source.slice(opts.streamer.length); +1780 } else { +1781 /* other providers type, video for instance - +1782 pass everything as is */ +1783 } +1784 +1785 pop = IriSP.PopcornReplacement.jwplayer("#" + containerDiv, opts); +1786 break; +1787 +1788 case "youtube": +1789 var opts = IriSP.jQuery.extend({}, options); +1790 delete opts.container; +1791 opts.controls = 0; +1792 opts.autostart = false; +1793 templ = "width: {{width}}px; height: {{height}}px;"; +1794 var str = Mustache.to_html(templ, {width: opts.width, height: opts.height}); +1795 // Popcorn.youtube wants us to specify the size of the player in the style attribute of its container div. +1796 IriSP.jQuery("#" + containerDiv).attr("style", str); +1797 +1798 pop = Popcorn.youtube("#" + containerDiv, opts.video, opts); +1799 break; +1800 +1801 default: +1802 pop = undefined; +1803 }; +1804 +1805 return pop; +1806 }; +1807 +1808 IriSP.configureWidgets = function (popcornInstance, layoutManager, guiOptions) { +1809 +1810 var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader); +1811 var params = {width: guiOptions.width, height: guiOptions.height}; +1812 +1813 var ret_widgets = []; +1814 var index; +1815 +1816 for (index = 0; index < guiOptions.widgets.length; index++) { +1817 var widgetConfig = guiOptions.widgets[index]; +1818 var widget = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, widgetConfig); +1819 ret_widgets.push(widget); +1820 +1821 }; +1822 +1823 return ret_widgets; +1824 }; +1825 +1826 IriSP.configureModules = function (popcornInstance, modulesList) { +1827 +1828 var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader); +1829 var ret_modules = []; +1830 var index; +1831 +1832 for (index = 0; index < modulesList.length; index++) { +1833 var moduleConfig = modulesList[index]; +1834 +1835 var serializer = serialFactory.getSerializer(moduleConfig.metadata); +1836 var module = new IriSP[moduleConfig.type](popcornInstance, moduleConfig, serializer); +1837 ret_modules.push(module); +1838 }; +1839 +1840 return ret_modules; +1841 }; +1842 +1843 IriSP.instantiateWidget = function(popcornInstance, serialFactory, layoutManager, widgetConfig) { +1844 /* create div returns us a container for the widget and a spacer */ +1845 var ret = layoutManager.createDiv(widgetConfig.type); +1846 var container = ret[0]; +1847 var spacer = ret[1]; +1848 +1849 var arr = IriSP.jQuery.extend({}, widgetConfig); +1850 arr.container = container; +1851 arr.spacer = spacer; +1852 +1853 var serializer = serialFactory.getSerializer(widgetConfig.metadata); +1854 +1855 if (typeof serializer == "undefined") +1856 debugger; +1857 +1858 // instantiate the object passed as a string +1859 var widget = new IriSP[widgetConfig.type](popcornInstance, arr, serializer); +1860 +1861 if (widgetConfig.hasOwnProperty("requires")) { +1862 // also create the widgets this one depends on. +1863 // the dependency widget is available in the parent widget context as +1864 // this.WidgetName (for instance, this.TipWidget); +1865 +1866 var i = 0; +1867 for(i = 0; i < widgetConfig.requires.length; i++) { +1868 var widgetName = widgetConfig.requires[i]["type"]; +1869 widget[widgetName] = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, widgetConfig.requires[i]); +1870 } +1871 } +1872 +1873 serializer.sync(IriSP.wrap(widget, function() { this.draw(); })); +1874 return widget; +1875 }; +1876 /* mediafragment module */ +1877 +1878 IriSP.MediaFragment = function(Popcorn, config, Serializer) { +1879 IriSP.Module.call(this, Popcorn, config, Serializer); +1880 +1881 this.mutex = false; /* a mutex because we access the url from two different functions */ +1882 +1883 this._Popcorn.listen( "loadedmetadata", IriSP.wrap(this, IriSP.MediaFragment.advanceTime)); +1884 this._Popcorn.listen( "pause", IriSP.wrap(this, IriSP.MediaFragment.updateTime)); +1885 this._Popcorn.listen( "seeked", IriSP.wrap(this, IriSP.MediaFragment.updateTime)); +1886 this._Popcorn.listen( "IriSP.PolemicTweet.click", IriSP.wrap(this, IriSP.MediaFragment.updateAnnotation)); +1887 this._Popcorn.listen( "IriSP.SegmentsWidget.click", IriSP.wrap(this, IriSP.MediaFragment.updateAnnotation)); +1888 }; +1889 +1890 IriSP.MediaFragment.advanceTime = function() { +1891 var url = window.location.href; +1892 +1893 if ( url.split( "#" )[ 1 ] != null ) { +1894 pageoffset = url.split( "#" )[1]; +1895 +1896 if ( pageoffset.substring(0, 2) === "t=") { +1897 // timecode +1898 if ( pageoffset.substring( 2 ) != null ) { +1899 var offsettime = pageoffset.substring( 2 ); +1900 this._Popcorn.currentTime( parseFloat( offsettime ) ); +1901 } +1902 } else if ( pageoffset.substring(0, 2) === "a=") { +1903 // annotation +1904 var annotationId = pageoffset.substring( 2 ); +1905 +1906 // there's no better way than that because +1907 // of possible race conditions +1908 this._serializer.sync(IriSP.wrap(this, function() { +1909 IriSP.MediaFragment.lookupAnnotation.call(this, annotationId); +1910 })); +1911 } +1912 } +1913 }; +1914 +1915 IriSP.MediaFragment.updateTime = function() { +1916 if (this.mutex === true) { +1917 return; +1918 } +1919 +1920 var history = window.history; +1921 if ( !history.pushState ) { +1922 return false; +1923 } +1924 +1925 splitArr = window.location.href.split( "#" ) +1926 history.replaceState( {}, "", splitArr[0] + "#t=" + this._Popcorn.currentTime().toFixed( 2 ) ); +1927 }; +1928 +1929 +1930 IriSP.MediaFragment.updateAnnotation = function(annotationId) { +1931 var _this = this; +1932 this.mutex = true; +1933 +1934 var history = window.history; +1935 if ( !history.pushState ) { +1936 return false; +1937 } +1938 +1939 splitArr = window.location.href.split( "#" ) +1940 history.replaceState( {}, "", splitArr[0] + "#a=" + annotationId); +1941 +1942 window.setTimeout(function() { _this.mutex = false }, 50); +1943 }; +1944 +1945 // lookup and seek to the beginning of an annotation +1946 IriSP.MediaFragment.lookupAnnotation = function(annotationId) { +1947 var annotation = undefined; +1948 var annotations = this._serializer._data.annotations; +1949 +1950 var i; +1951 for (i = 0; i < annotations.length; i++) { +1952 if (annotations[i].id === annotationId) { +1953 annotation = annotations[i]; +1954 break; +1955 } +1956 } +1957 +1958 if (typeof(annotation) !== "undefined") { +1959 this._Popcorn.currentTime(annotation.begin / 1000); +1960 } +1961 }; +1962 IriSP.AnnotationsWidget = function(Popcorn, config, Serializer) { +1963 IriSP.Widget.call(this, Popcorn, config, Serializer); +1964 +1965 }; +1966 +1967 +1968 IriSP.AnnotationsWidget.prototype = new IriSP.Widget(); +1969 +1970 IriSP.AnnotationsWidget.prototype.clear = function() { +1971 this.selector.find(".Ldt-SaTitle").text(""); +1972 this.selector.find(".Ldt-SaDescription").text(""); +1973 this.selector.find(".Ldt-SaKeywordText").text(""); +1974 }; +1975 +1976 IriSP.AnnotationsWidget.prototype.displayAnnotation = function(annotation) { +1977 +1978 var title = annotation.content.title; +1979 var description = annotation.content.description; +1980 var keywords = "" // FIXME; +1981 var begin = +annotation.begin / 1000; +1982 var end = +annotation.end / 1000; +1983 var duration = +this._serializer.currentMedia().meta["dc:duration"]; +1984 +1985 var title_templ = "{{title}} - ( {{begin}} - {{end}} )"; +1986 var endstr = Mustache.to_html(title_templ, {title: title, begin: IriSP.secondsToTime(begin), end: IriSP.secondsToTime(end)}); +1987 +1988 this.selector.find(".Ldt-SaTitle").text(endstr); +1989 this.selector.find(".Ldt-SaDescription").text(description); +1990 +1991 // update sharing buttons +1992 var defaults = IriSP.widgetsDefaults.AnnotationsWidget; +1993 var text = defaults.share_text; +1994 var fb_link = defaults.fb_link; +1995 var tw_link = defaults.tw_link; +1996 var gplus_link = defaults.gplus_link; +1997 var url = document.location.href + "#a=" + annotation.id; +1998 this.selector.find(".Ldt-fbShare").attr("href", fb_link + IriSP.encodeURI(text) + IriSP.encodeURI(url)); +1999 this.selector.find(".Ldt-TwShare").attr("href", tw_link + IriSP.encodeURI(text) + IriSP.encodeURI(url)); +2000 this.selector.find(".Ldt-GplusShare").attr("href", fb_link + IriSP.encodeURI(text) + IriSP.encodeURI(url)); +2001 }; +2002 +2003 IriSP.AnnotationsWidget.prototype.clearWidget = function() { +2004 +2005 +2006 /* retract the pane between two annotations */ +2007 this.selector.find(".Ldt-SaTitle").text(""); +2008 this.selector.find(".Ldt-SaDescription").text(""); +2009 this.selector.find(".Ldt-SaKeywordText").html(""); +2010 this.selector.find(".Ldt-ShowAnnotation").slideUp(); +2011 }; +2012 +2013 IriSP.AnnotationsWidget.prototype.draw = function() { +2014 var _this = this; +2015 +2016 var annotationMarkup = IriSP.templToHTML(IriSP.annotationWidget_template); +2017 this.selector.append(annotationMarkup); +2018 var view; +2019 +2020 if (typeof(this._serializer._data.views) !== "undefined" && this._serializer._data.views !== null) +2021 view = this._serializer._data.views[0]; +2022 +2023 var view_type = ""; +2024 +2025 if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) { +2026 view_type = view.annotation_types[0]; +2027 } +2028 +2029 var annotations = this._serializer._data.annotations; +2030 var i; +2031 +2032 for (i in annotations) { +2033 var annotation = annotations[i]; +2034 var begin = Math.round((+ annotation.begin) / 1000); +2035 var end = Math.round((+ annotation.end) / 1000); +2036 +2037 if (view_type != "" && typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined" +2038 && annotation.meta["id-ref"] != view_type) { +2039 continue; +2040 } +2041 +2042 +2043 var conf = {start: begin, end: end, +2044 onStart: +2045 function(annotation) { +2046 return function() { +2047 _this.displayAnnotation(annotation); +2048 +2049 } }(annotation), +2050 onEnd: +2051 function() { _this.clearWidget.call(_this); } +2052 }; +2053 this._Popcorn = this._Popcorn.code(conf); +2054 } +2055 +2056 }; +2057 IriSP.ArrowWidget = function(Popcorn, config, Serializer) { +2058 IriSP.Widget.call(this, Popcorn, config, Serializer); +2059 +2060 this._oldAnnotation = null; +2061 +2062 }; +2063 +2064 +2065 IriSP.ArrowWidget.prototype = new IriSP.Widget(); +2066 +2067 IriSP.ArrowWidget.prototype.clear = function() { +2068 +2069 }; +2070 +2071 IriSP.ArrowWidget.prototype.clearWidget = function() { +2072 }; +2073 +2074 IriSP.ArrowWidget.prototype.draw = function() { +2075 var templ = Mustache.to_html(IriSP.arrowWidget_template, {}); +2076 this.selector.append(templ); +2077 this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler)); +2078 }; +2079 +2080 IriSP.ArrowWidget.prototype.timeUpdateHandler = function(percents) { +2081 var currentTime = this._Popcorn.currentTime(); +2082 var currentAnnotation = this._serializer.currentAnnotations(currentTime)[0]; // FIXME : use the others ? +2083 +2084 /* move the arrow only if the current annotation changes */ +2085 if (currentAnnotation != this._oldAnnotation) { +2086 var begin = (+ currentAnnotation.begin) / 1000; +2087 var end = (+ currentAnnotation.end) / 1000; +2088 +2089 var duration = +this._serializer.currentMedia().meta["dc:duration"] / 1000; +2090 var middle_time = (begin + end) / 2; +2091 var percents = Math.floor((middle_time / duration) * 100); +2092 +2093 // we need to apply a fix because the arrow has a certain length +2094 // it's half the length of the arrow (27 / 2). We need to convert +2095 // it in percents though. +2096 var totalWidth = this.selector.width(); +2097 var correction = ((27 / 2) / totalWidth) * 100; +2098 var corrected_percents = percents - correction; +2099 +2100 /* don't move out of the screen */ +2101 if (corrected_percents <= 0) +2102 corrected_percents = 0; +2103 +2104 this.selector.children(".Ldt-arrowWidget").animate({"left" : corrected_percents + "%"}); +2105 +2106 this._oldAnnotation = currentAnnotation; +2107 } +2108 } +2109 IriSP.PlayerWidget = function(Popcorn, config, Serializer) { +2110 IriSP.Widget.call(this, Popcorn, config, Serializer); +2111 +2112 this._searchBlockOpen = false; +2113 this._searchLastValue = ""; +2114 }; +2115 +2116 IriSP.PlayerWidget.prototype = new IriSP.Widget(); +2117 +2118 IriSP.PlayerWidget.prototype.draw = function() { +2119 var self = this; +2120 var width = this.width; +2121 var height = this.height; +2122 var heightS = this.height-20; +2123 +2124 var Player_templ = Mustache.to_html(IriSP.player_template, {"share_template" : IriSP.share_template}); +2125 this.selector.append(Player_templ); +2126 +2127 this.selector.children(".Ldt-controler").show(); +2128 +2129 // handle clicks by the user on the video. +2130 this._Popcorn.listen("play", IriSP.wrap(this, this.playButtonUpdater)); +2131 this._Popcorn.listen("pause", IriSP.wrap(this, this.playButtonUpdater)); +2132 +2133 this._Popcorn.listen("volumechange", IriSP.wrap(this, this.muteButtonUpdater)); +2134 +2135 this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeDisplayUpdater)); +2136 this._Popcorn.listen("IriSP.search.matchFound", IriSP.wrap(this, this.searchMatch)); +2137 this._Popcorn.listen("IriSP.search.noMatchFound", IriSP.wrap(this, this.searchNoMatch)); +2138 +2139 +2140 this.selector.find(".Ldt-CtrlPlay").click(function() { self.playHandler.call(self); }); +2141 this.selector.find(".Ldt-CtrlNext").click(function() { }); +2142 this.selector.find(".Ldt-CtrlSearch").click(function() { self.searchButtonHandler.call(self); }); +2143 +2144 this.selector.find('.Ldt-CtrlSound').click(function() { self.muteHandler.call(self); } ); +2145 +2146 this.selector.find(".Ldt-CtrlPlay").attr( "style", "background-color:#CD21C24;" ); +2147 +2148 var searchButtonPos = this.selector.find(".Ldt-CtrlSearch").position(); +2149 var searchBox = Mustache.to_html(IriSP.search_template, {margin_left : searchButtonPos.left + "px"}); +2150 this.selector.append(searchBox); +2151 +2152 // trigger an IriSP.PlayerWidget.MouseOver to the widgets that are interested (i.e : sliderWidget) +2153 this.selector.hover(function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOver"); }, +2154 function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOut"); }); +2155 +2156 this.muteButtonUpdater(); /* some player - jwplayer notable - save the state of the mute button between sessions */ +2157 }; +2158 +2159 /* Update the elasped time div */ +2160 IriSP.PlayerWidget.prototype.timeDisplayUpdater = function() { +2161 +2162 if (this._previousSecond === undefined) +2163 this._previousSecond = this._Popcorn.roundTime(); +2164 +2165 else { +2166 /* we're still in the same second, so it's not necessary to update time */ +2167 if (this._Popcorn.roundTime() == this._previousSecond) +2168 return; +2169 +2170 } +2171 +2172 // we get it at each call because it may change. +2173 var duration = +this._serializer.currentMedia().meta["dc:duration"] / 1000; +2174 var totalTime = IriSP.secondsToTime(duration); +2175 var elapsedTime = IriSP.secondsToTime(this._Popcorn.currentTime()); +2176 +2177 this.selector.find(".Ldt-ElapsedTime").html(elapsedTime.toString()); +2178 this.selector.find(".Ldt-TotalTime").html(totalTime.toString()); +2179 this._previousSecond = this._Popcorn.roundTime(); +2180 }; +2181 +2182 /* update the icon of the button - separate function from playHandler +2183 because in some cases (for instance, when the user directly clicks on +2184 the jwplayer window) we have to change the icon without playing/pausing +2185 */ +2186 IriSP.PlayerWidget.prototype.playButtonUpdater = function() { +2187 var status = this._Popcorn.media.paused; +2188 +2189 if ( status == true ){ +2190 this.selector.find(".Ldt-CtrlPlay").attr("title", "Play"); +2191 +2192 // we use templToHTML because it has some predefined +2193 // vars like where to get the images +2194 var templ = IriSP.templToHTML("url({{img_dir}}/play_sprite.png)"); +2195 this.selector.find(".Ldt-CtrlPlay").css("background-image", templ); +2196 +2197 } else { +2198 this.selector.find(".Ldt-CtrlPlay").attr("title", "Pause"); +2199 +2200 // we use templToHTML because it has some predefined +2201 // vars like where to get the images +2202 var templ = IriSP.templToHTML("url({{img_dir}}/pause_sprite.png)"); +2203 this.selector.find(".Ldt-CtrlPlay").css("background-image", templ); +2204 } +2205 +2206 return; +2207 }; +2208 +2209 +2210 IriSP.PlayerWidget.prototype.playHandler = function() { +2211 var status = this._Popcorn.media.paused; +2212 +2213 if ( status == true ){ +2214 this._Popcorn.play(); +2215 } else { +2216 this._Popcorn.pause(); +2217 } +2218 }; +2219 +2220 IriSP.PlayerWidget.prototype.muteHandler = function() { +2221 if (!this._Popcorn.muted()) { +2222 this._Popcorn.mute(true); +2223 } else { +2224 this._Popcorn.mute(false); +2225 } +2226 }; +2227 +2228 IriSP.PlayerWidget.prototype.muteButtonUpdater = function() { +2229 var status = this._Popcorn.media.muted; +2230 +2231 if ( status == true ){ +2232 this.selector.find(".Ldt-CtrlSound").attr("title", "Unmute"); +2233 +2234 // we use templToHTML because it has some predefined +2235 // vars like where to get the images +2236 var templ = IriSP.templToHTML("url({{img_dir}}/sound_sprite.png)"); +2237 this.selector.find(".Ldt-CtrlSound").css("background-image", templ); +2238 +2239 } else { +2240 this.selector.find(".Ldt-CtrlSound").attr("title", "Mute"); +2241 +2242 // we use templToHTML because it has some predefined +2243 // vars like where to get the images +2244 var templ = IriSP.templToHTML("url({{img_dir}}/mute_sprite.png)"); +2245 this.selector.find(".Ldt-CtrlSound").css("background-image", templ); +2246 } +2247 +2248 return; +2249 }; +2250 +2251 +2252 IriSP.PlayerWidget.prototype.searchButtonHandler = function() { +2253 var self = this; +2254 +2255 /* show the search field if it is not shown */ +2256 if ( this._searchBlockOpen == false ) { +2257 this.selector.find(".LdtSearch").show(100); +2258 +2259 this.selector.find(".LdtSearchInput").css('background-color','#fff'); +2260 this.selector.find(".LdtSearchInput").focus(); +2261 this.selector.find(".LdtSearchInput").attr('value', this._searchLastValue); +2262 this._Popcorn.trigger("IriSP.search", this._searchLastValue); // trigger the search to make it more natural. +2263 +2264 this._searchBlockOpen = true; +2265 this.selector.find(".LdtSearchInput").bind('keyup', null, function() { self.searchHandler.call(self); } ); +2266 +2267 // we need this variable because some widget can find a match in +2268 // their data while at the same time other's don't. As we want the +2269 // search field to become green when there's a match, we need a +2270 // variable to remember that we had one. +2271 this._positiveMatch = false; +2272 +2273 // tell the world the field is open +2274 this._Popcorn.trigger("IriSP.search.open"); +2275 +2276 } else { +2277 this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value'); +2278 this.selector.find(".LdtSearchInput").attr('value',''); +2279 this.selector.find(".LdtSearch").hide(100); +2280 +2281 // unbind the watcher event. +2282 this.selector.find(".LdtSearchInput").unbind('keypress set'); +2283 this._searchBlockOpen = false; +2284 +2285 this._positiveMatch = false; +2286 +2287 this._Popcorn.trigger("IriSP.search.closed"); +2288 } +2289 }; +2290 +2291 /* this handler is called whenever the content of the search +2292 field changes */ +2293 IriSP.PlayerWidget.prototype.searchHandler = function() { +2294 this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value'); +2295 this._positiveMatch = false; +2296 +2297 // do nothing if the search field is empty, instead of highlighting everything. +2298 if (this._searchLastValue == "") { +2299 this._Popcorn.trigger("IriSP.search.cleared"); +2300 this.selector.find(".LdtSearchInput").css('background-color',''); +2301 } else { +2302 this._Popcorn.trigger("IriSP.search", this._searchLastValue); +2303 } +2304 }; +2305 +2306 /* +2307 handler for the IriSP.search.found message, which is sent by some views when they +2308 highlight a match. +2309 */ +2310 IriSP.PlayerWidget.prototype.searchMatch = function() { +2311 this._positiveMatch = true; +2312 this.selector.find(".LdtSearchInput").css('background-color','#e1ffe1'); +2313 } +2314 +2315 /* the same, except that no value could be found */ +2316 IriSP.PlayerWidget.prototype.searchNoMatch = function() { +2317 if (this._positiveMatch !== true) +2318 this.selector.find(".LdtSearchInput").css('background-color', "#d62e3a"); +2319 } +2320 +2321 /* +2322 * +2323 * Copyright 2010 Institut de recherche et d'innovation +2324 * contributor(s) : Samuel Huron +2325 * +2326 * contact@iri.centrepompidou.fr +2327 * http://www.iri.centrepompidou.fr +2328 * +2329 * This software is a computer program whose purpose is to show and add annotations on a video . +2330 * This software is governed by the CeCILL-C license under French law and +2331 * abiding by the rules of distribution of free software. You can use, +2332 * modify and/ or redistribute the software under the terms of the CeCILL-C +2333 * license as circulated by CEA, CNRS and INRIA at the following URL +2334 * "http://www.cecill.info". +2335 * +2336 * The fact that you are presently reading this means that you have had +2337 * knowledge of the CeCILL-C license and that you accept its terms. +2338 */ +2339 // CHART TIMELINE / VERSION PROTOTYPE :: +2340 +2341 IriSP.PolemicWidget = function(Popcorn, config, Serializer) { +2342 IriSP.Widget.call(this, Popcorn, config, Serializer); +2343 +2344 this.userPol = new Array(); +2345 this.userNoPol = new Array(); +2346 this.userst = new Array(); +2347 this.numberOfTweet = 0; +2348 this.Users; +2349 this.TweetPolemic; +2350 this.yMax = this.height; +2351 this.PaperSlider; +2352 this.heightOfChart; +2353 this.tweets = new Array(); +2354 this.svgElements = {}; +2355 +2356 // Make and define the Raphael area +2357 this.paper = Raphael(document.getElementById(this._id), config.width, config.height); +2358 +2359 this.oldSearchMatches = []; +2360 +2361 // event handlers +2362 this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) { this.searchHandler(searchString); })); +2363 this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.searchFieldClosedHandler)); +2364 this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.searchFieldClearedHandler)); +2365 +2366 }; +2367 +2368 IriSP.PolemicWidget.prototype = new IriSP.Widget(); +2369 +2370 IriSP.PolemicWidget.prototype.draw = function() { +2371 +2372 // variable +2373 // yMax +2374 +2375 var self = this; +2376 var yCoef = 2; // coef for height of 1 tweet +2377 var frameSize = 5; // frame size +2378 var margin = 1; // marge between frame +2379 var lineSize = this.width; // timeline pixel width +2380 var nbrframes = lineSize/frameSize; // frame numbers +2381 var numberOfTweet = 0; // number of tweet overide later +2382 var duration = +this._serializer.currentMedia().meta["dc:duration"]; // timescale width +2383 var frameLength = lineSize / frameSize; // frame timescale +2384 var timeline; +2385 var colors = new Array("","#1D973D","#C5A62D","#CE0A15","#036AAE","#585858"); +2386 +2387 // array +2388 //var tweets = new Array(); +2389 var element = new Array(); +2390 var cluster = new Array(); +2391 var frames = new Array(frameLength); +2392 var slices = new Array(); +2393 +2394 +2395 // Classes ======================================================================= +2396 var Frames = function(){ +2397 +2398 var Myclusters; +2399 var x; +2400 var y; +2401 var width; +2402 var height; +2403 }; +2404 Frames = function(json){ +2405 // make my clusters +2406 // ou Frame vide +2407 }; +2408 Frames.prototype.draw = function(){ +2409 }; +2410 Frames.prototype.zoom = function(){ +2411 }; +2412 Frames.prototype.inside = function(){ +2413 }; +2414 var Clusters = function(){ +2415 var Object; +2416 var yDist; +2417 var x; +2418 var y; +2419 var width; +2420 var height; +2421 }; +2422 Clusters = function(json){ +2423 // make my object +2424 }; +2425 var Tweet = function(){ +2426 }; +2427 // Classes ======================================================================= +2428 +2429 // Refactoring (parametere) ************************************************************ +2430 // color translastion +2431 var qTweet_0 =0; +2432 var qTweet_Q =0; +2433 var qTweet_REF=0; +2434 var qTweet_OK =0; +2435 var qTweet_KO =0; +2436 function colorTranslation(value){ +2437 if(value == "Q"){ +2438 qTweet_Q+=1; +2439 return 2; +2440 }else if(value =="REF"){ +2441 qTweet_REF+=1; +2442 return 4; +2443 }else if(value =="OK"){ +2444 qTweet_OK+=1; +2445 return 1; +2446 }else if(value =="KO"){ +2447 qTweet_KO+=1; +2448 return 3; +2449 }else if(value ==""){ +2450 qTweet_0+=1; +2451 return 5; +2452 } +2453 } +2454 +2455 +2456 this._serializer.sync(function(data) { loaded_callback.call(self, data) }); +2457 +2458 function loaded_callback (json) { +2459 +2460 // get current view (the first ???) +2461 view = json.views[0]; +2462 +2463 // the tweets are by definition of the second annotation type FIXME ? +2464 tweet_annot_type = null; +2465 if(typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) { +2466 tweet_annot_type = view.annotation_types[1]; +2467 } +2468 +2469 for(var i = 0; i < json.annotations.length; i++) { +2470 var item = json.annotations[i]; +2471 var MyTime = Math.floor(item.begin/duration*lineSize); +2472 var Myframe = Math.floor(MyTime/lineSize*frameLength); +2473 +2474 if (typeof(item.meta) !== "undefined" +2475 && typeof(item.meta["id-ref"]) !== "undefined" +2476 && item.meta["id-ref"] === tweet_annot_type) { +2477 +2478 var MyTJson = JSON.parse(item.meta['dc:source']['content']); +2479 +2480 if (item.content['polemics'] != undefined +2481 && item.content['polemics'][0] != null) { +2482 +2483 // a tweet can have many polemics at the same time. +2484 for(var j=0; j<item.content['polemics'].length; j++){ +2485 +2486 this.tweets[numberOfTweet] = { +2487 id:i, +2488 qualification:colorTranslation(item.content['polemics'][j]), +2489 yIndicator:MyTime, +2490 yframe:Myframe, +2491 title:item.content['title'], +2492 timeframe:item.begin, +2493 userId: MyTJson.id, +2494 userScreenName: MyTJson.screen_name, +2495 tsource:MyTJson, +2496 cinecast_id: item.id +2497 }; +2498 numberOfTweet+=1; +2499 +2500 } +2501 } +2502 else { +2503 this.tweets[numberOfTweet] = { +2504 id:i, +2505 qualification:colorTranslation(""), +2506 yIndicator:MyTime, +2507 yframe:Myframe, +2508 title:item.content['title'], +2509 timeframe:item.begin, +2510 userId: MyTJson.id, +2511 userScreenName: MyTJson.screen_name, +2512 tsource:MyTJson, +2513 cinecast_id: item.id +2514 }; +2515 numberOfTweet+=1; +2516 } +2517 +2518 } +2519 }; +2520 +2521 DrawTweets.call (this); // FIXME: ugly. +2522 +2523 }; +2524 +2525 // tweet Drawing (in raphael) +2526 function DrawTweets (){ +2527 // GROUPES TWEET ============================================ +2528 // Count nbr of cluster and tweet in a frame an save int in "frames" +2529 numberOfTweet = this.tweets.length; +2530 for(var i=0; i<nbrframes; i++) { +2531 for(var j=0; j<numberOfTweet; j++) { +2532 +2533 if (i==this.tweets[j].yframe){ +2534 +2535 var k = this.tweets[j].qualification; +2536 +2537 // make array for frame cluster +2538 if(frames[i]==undefined){ +2539 frames[i] = {id:i, +2540 qualifVol:new Array(), +2541 mytweetsID:new Array() +2542 }; +2543 } +2544 // add my tweet to frame +2545 frames[i].mytweetsID.push(this.tweets[j]); +2546 +2547 // count opinion by frame +2548 if( frames[i].qualifVol[k] == undefined){ +2549 frames[i].qualifVol[k] = 1; +2550 }else{ +2551 frames[i].qualifVol[k] += 1; +2552 } +2553 +2554 } +2555 } +2556 } +2557 +2558 // GROUPES TWEET ============================================ +2559 // max of tweet by Frame +2560 var max = 0; +2561 for(var i = 0; i < nbrframes; i++) { +2562 var moy = 0; +2563 for (var j = 0; j < 6; j++) { +2564 if (frames[i] != undefined) { +2565 if (frames[i].qualifVol[j] != undefined) { +2566 moy += frames[i].qualifVol[j]; +2567 } +2568 } +2569 } +2570 +2571 if (moy > max) { +2572 max = moy; +2573 } +2574 } +2575 +2576 var tweetDrawed = new Array(); +2577 var TweetHeight = 5; +2578 +2579 // DRAW TWEETS ============================================ +2580 for(var i = 0; i < nbrframes; i++) { +2581 var addEheight = 5; +2582 if (frames[i] != undefined){ +2583 // by type +2584 +2585 for (var j = 6; j > -1; j--) { +2586 if (frames[i].qualifVol[j] != undefined) { +2587 // show tweet by type +2588 for (var k = 0; k < frames[i].mytweetsID.length; k++) { +2589 +2590 if (frames[i].mytweetsID[k].qualification == j) { +2591 var x = i * frameSize; +2592 var y = this.heightmax - addEheight; +2593 +2594 if (this.yMax > y) { +2595 this.yMax = y; +2596 } +2597 +2598 var e = this.paper.rect(x, y, frameSize - margin, TweetHeight /* height */) +2599 .attr({stroke:"#00","stroke-width":0.1, fill: colors[j]}); +2600 +2601 addEheight += TweetHeight; +2602 +2603 e.color = colors[j]; +2604 e.time = frames[i].mytweetsID[k].timeframe; +2605 e.title = frames[i].mytweetsID[k].title; +2606 e.id = frames[i].mytweetsID[k].cinecast_id; +2607 +2608 this.svgElements[e.id] = e; +2609 +2610 /* +2611 e.mouseover(function(element) { return function (event) { +2612 // event.clientX and event.clientY are to raphael what event.pageX and pageY are to jquery. +2613 self.TooltipWidget.show.call(self.TooltipWidget, element.title, element.attr("fill"), event.clientX - 106, event.clientY - 160); +2614 element.displayed = true; +2615 }}(e)).mouseout(function(element) { return function () { +2616 self.TooltipWidget.hide.call(self.TooltipWidget); +2617 }}(e)).mousedown(function () { +2618 self._Popcorn.currentTime(this.time/1000); +2619 self._Popcorn.trigger("IriSP.PolemicTweet.click", this.id); +2620 }); +2621 */ +2622 +2623 IriSP.jQuery(e.node).mouseenter(function(element) { return function (event) { +2624 // event.clientX and event.clientY are to raphael what event.pageX and pageY are to jquery. +2625 self.TooltipWidget.show.call(self.TooltipWidget, element.title, element.attr("fill"), event.pageX - 106, event.pageY - 160); +2626 element.displayed = true; +2627 }}(e)).mousedown(function(element) { return function () { +2628 self._Popcorn.currentTime(element.time/1000); +2629 self._Popcorn.trigger("IriSP.PolemicTweet.click", element.id); +2630 } +2631 }(e)); +2632 +2633 IriSP.jQuery(e.node).attr('id', 't' + k + ''); +2634 IriSP.jQuery(e.node).attr('title', frames[i].mytweetsID[k].title); +2635 IriSP.jQuery(e.node).attr('begin', frames[i].mytweetsID[k].timeframe); +2636 } +2637 } +2638 } +2639 } +2640 } +2641 +2642 } +2643 // DRAW UI :: resize border and bgd +2644 this.paperBackground = this.paper.rect(0, 0, this.width, this.heightmax).attr({fill:"#F8F8F8","stroke-width":0.1,opacity: 1}); +2645 +2646 // outer borders +2647 this.outerBorders = []; +2648 this.outerBorders.push(this.paper.rect(0, this.height - 1, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1})); +2649 this.outerBorders.push(this.paper.rect(0, 0, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1})); +2650 +2651 // inner borders +2652 this.innerBorders = []; +2653 this.innerBorders.push(this.paper.rect(1, this.height - 2, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1})); +2654 this.innerBorders.push(this.paper.rect(1, 1, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1})); +2655 this.innerBorders.push(this.paper.rect(1, 1, 1, this.height - 2).attr({fill:"#d0d1d1",stroke: "none",opacity: 0.8})); +2656 this.innerBorders.push(this.paper.rect(this.width - 2, 1, 1, this.height - 2).attr({fill:"#efefef",stroke: "none",opacity: 1})); +2657 +2658 +2659 +2660 this.paperSlider = this.paper.rect(0, 0, 0, this.heightmax).attr({fill:"#D4D5D5", stroke: "none", opacity: 1}); +2661 +2662 // the small white line displayed over the slider. +2663 this.sliderTip = this.paper.rect(0, 0, 1, this.heightmax).attr({fill:"#fc00ff", stroke: "none", opacity: 1}); +2664 // decalage +2665 // tweetSelection = this.paper.rect(-100,-100,5,5).attr({fill:"#fff",stroke: "none",opacity: 1}); +2666 +2667 +2668 this.paperSlider.toBack(); +2669 this.paperBackground.toBack(); +2670 this.sliderTip.toFront(); +2671 } +2672 +2673 this.selector.mouseleave(IriSP.wrap(this, function() { self.TooltipWidget.hide.call(self.TooltipWidget); })); +2674 this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater)); +2675 } +2676 +2677 IriSP.PolemicWidget.prototype.sliderUpdater = function() { +2678 +2679 var time = +this._Popcorn.currentTime(); +2680 var duration = +this._serializer.currentMedia().meta["dc:duration"]; +2681 +2682 this.paperSlider.attr("width", time * (this.width / (duration / 1000))); +2683 +2684 this.sliderTip.attr("x", time * (this.width / (duration / 1000))); +2685 }; +2686 +2687 IriSP.PolemicWidget.prototype.searchHandler = function(searchString) { +2688 if (searchString == "") +2689 return; +2690 +2691 var matches = this._serializer.searchTweetsOccurences(searchString); +2692 +2693 if (IriSP.countProperties(matches) > 0) { +2694 this._Popcorn.trigger("IriSP.search.matchFound"); +2695 } else { +2696 this._Popcorn.trigger("IriSP.search.noMatchFound"); +2697 } +2698 +2699 for (var id in matches) { +2700 if (this.svgElements.hasOwnProperty(id)) { +2701 var e = this.svgElements[id]; +2702 this.svgElements[id].attr({fill: "#fc00ff"}); +2703 } +2704 } +2705 +2706 // clean up the blocks that were in the previous search +2707 // but who aren't in the current one. +2708 for (var id in this.oldSearchMatches) { +2709 if (!matches.hasOwnProperty(id)) { +2710 var e = this.svgElements[id]; +2711 e.attr({fill: e.color}); +2712 } +2713 } +2714 +2715 this.oldSearchMatches = matches; +2716 }; +2717 +2718 IriSP.PolemicWidget.prototype.searchFieldClearedHandler = function() { +2719 // clean up the blocks that were in the previous search +2720 // but who aren't in the current one. +2721 for (var id in this.oldSearchMatches) { +2722 var e = this.svgElements[id]; +2723 e.attr({fill: e.color}); +2724 } +2725 +2726 }; +2727 +2728 IriSP.PolemicWidget.prototype.searchFieldClosedHandler = function() { +2729 // clean up the blocks that were in the previous search +2730 // but who aren't in the current one. +2731 for (var id in this.oldSearchMatches) { +2732 var e = this.svgElements[id]; +2733 e.attr({fill: e.color}); +2734 } +2735 +2736 }; +2737 +2738 IriSP.SegmentsWidget = function(Popcorn, config, Serializer) { +2739 +2740 var self = this; +2741 IriSP.Widget.call(this, Popcorn, config, Serializer); +2742 this.oldSearchMatches = []; +2743 +2744 // event handlers +2745 this._Popcorn.listen("IriSP.search", function(searchString) { self.searchHandler.call(self, searchString); }); +2746 this._Popcorn.listen("IriSP.search.closed", function() { self.searchFieldClosedHandler.call(self); }); +2747 this._Popcorn.listen("IriSP.search.cleared", function() { self.searchFieldClearedHandler.call(self); }); +2748 }; +2749 +2750 IriSP.SegmentsWidget.prototype = new IriSP.Widget(); +2751 +2752 /* Get the width of a segment, in pixels. */ +2753 IriSP.SegmentsWidget.prototype.segmentToPixel = function(annotation) { +2754 var begin = Math.round((+ annotation.begin) / 1000); +2755 var end = Math.round((+ annotation.end) / 1000); +2756 var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000; +2757 +2758 var startPourcent = IriSP.timeToPourcent(begin, duration); +2759 var startPixel = Math.floor(this.selector.parent().width() * (startPourcent / 100)); +2760 +2761 var endPourcent = Math.floor(IriSP.timeToPourcent(end, duration) - startPourcent); +2762 var endPixel = Math.floor(this.selector.parent().width() * (endPourcent / 100)); +2763 +2764 return endPixel; +2765 }; +2766 +2767 /* compute the total length of a group of segments */ +2768 IriSP.SegmentsWidget.prototype.segmentsLength = function(segmentsList) { +2769 var self = this; +2770 var total = 0; +2771 +2772 for (var i = 0; i < segmentsList.length; i++) +2773 total += self.segmentToPixel(segmentsList[i].annotation); +2774 +2775 return total; +2776 }; +2777 +2778 IriSP.SegmentsWidget.prototype.draw = function() { +2779 +2780 var self = this; +2781 var annotations = this._serializer._data.annotations; +2782 +2783 this.selector.addClass("Ldt-SegmentsWidget"); +2784 this.selector.append(Mustache.to_html(IriSP.overlay_marker_template)); +2785 +2786 var view_type = this._serializer.getNonTweetIds()[0]; +2787 +2788 this.positionMarker = this.selector.children(":first"); +2789 +2790 this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.positionUpdater)); +2791 +2792 +2793 var i = 0; +2794 +2795 var segments_annotations = []; +2796 +2797 for (i = 0; i < annotations.length; i++) { +2798 var annotation = annotations[i]; +2799 +2800 /* filter the annotations whose type is not the one we want */ +2801 if (view_type != "" && typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined" +2802 && annotation.meta["id-ref"] != view_type) { +2803 continue; +2804 } +2805 +2806 segments_annotations.push(annotation); +2807 } +2808 +2809 var totalWidth = this.selector.width() - segments_annotations.length; +2810 var lastSegment = IriSP.underscore.max(segments_annotations, function(annotation) { return annotation.end; }); +2811 +2812 for (i = 0; i < segments_annotations.length; i++) { +2813 +2814 var annotation = segments_annotations[i]; +2815 var begin = (+ annotation.begin); +2816 var end = (+ annotation.end); +2817 var duration = this._serializer.currentMedia().meta["dc:duration"]; +2818 var id = annotation.id; +2819 +2820 var startPixel = Math.floor(this.selector.parent().width() * (begin / duration)); +2821 +2822 var endPixel = Math.floor(this.selector.parent().width() * (end / duration)); +2823 +2824 if (annotation.id !== lastSegment.id) +2825 var pxWidth = endPixel - startPixel -1; +2826 else +2827 /* the last segment has no segment following it */ +2828 var pxWidth = endPixel - startPixel; +2829 +2830 var divTitle = (annotation.content.title + " - " + annotation.content.description).substr(0,55); +2831 +2832 if (typeof(annotation.content.color) !== "undefined") +2833 var color = annotation.content.color; +2834 else +2835 var color = annotation.color; +2836 +2837 var hexa_color = IriSP.DEC_HEXA_COLOR(color); +2838 +2839 if (hexa_color === "FFCC00") +2840 hexa_color = "333"; +2841 if (hexa_color.length == 4) +2842 hexa_color = hexa_color + '00'; +2843 +2844 var annotationTemplate = Mustache.to_html(IriSP.annotation_template, +2845 {"divTitle" : divTitle, "id" : id, "startPixel" : startPixel, +2846 "pxWidth" : pxWidth, "hexa_color" : hexa_color, +2847 "seekPlace" : Math.round(begin/1000)}); +2848 +2849 +2850 this.selector.append(annotationTemplate); +2851 +2852 /* add a special class to the last segment and change its border */ +2853 if (annotation.id === lastSegment.id) { +2854 this.selector.find("#" + id).addClass("Ldt-lastSegment"); +2855 this.selector.find(".Ldt-lastSegment").css("border-color", "#" + hexa_color); +2856 } +2857 +2858 IriSP.jQuery("#" + id).fadeTo(0, 0.3); +2859 +2860 IriSP.jQuery("#" + id).mouseover( +2861 /* we wrap the handler in another function because js's scoping +2862 rules are function-based - otherwise, the internal vars like +2863 divTitle are preserved but they are looked-up from the draw +2864 method scope, so after that the loop is run, so they're not +2865 preserved */ +2866 (function(divTitle) { +2867 return function(event) { +2868 IriSP.jQuery(this).animate({opacity: 0.6}, 5); +2869 var offset = IriSP.jQuery(this).offset(); +2870 var correction = IriSP.jQuery(this).outerWidth() / 2; +2871 +2872 var offset_x = offset.left + correction - 106; +2873 if (offset_x < 0) +2874 offset_x = 0; +2875 +2876 self.TooltipWidget.show(divTitle, color, offset_x, event.pageY - 160); +2877 } })(divTitle)).mouseout(function(){ +2878 IriSP.jQuery(this).animate({opacity: 0.3}, 5); +2879 self.TooltipWidget.hide(); +2880 }); +2881 +2882 IriSP.jQuery("#" + id).click(function(_this, annotation) { +2883 return function() { _this.clickHandler(annotation)}; +2884 }(this, annotation)); +2885 } +2886 }; +2887 +2888 /* restores the view after a search */ +2889 IriSP.SegmentsWidget.prototype.clear = function() { +2890 this.selector.children(".Ldt-iri-chapter").animate({opacity:0.3}, 100); +2891 }; +2892 +2893 IriSP.SegmentsWidget.prototype.clickHandler = function(annotation) { +2894 this._Popcorn.trigger("IriSP.SegmentsWidget.click", annotation.id); +2895 var begin = (+ annotation.begin) / 1000; +2896 this._Popcorn.currentTime(Math.round(begin)); +2897 }; +2898 +2899 IriSP.SegmentsWidget.prototype.searchHandler = function(searchString) { +2900 +2901 if (searchString == "") +2902 return; +2903 +2904 var matches = this._serializer.searchOccurences(searchString); +2905 +2906 if (IriSP.countProperties(matches) > 0) { +2907 this._Popcorn.trigger("IriSP.search.matchFound"); +2908 } else { +2909 this._Popcorn.trigger("IriSP.search.noMatchFound"); +2910 } +2911 +2912 // un-highlight all the blocks +2913 this.selector.children(".Ldt-iri-chapter").css("opacity", 0.1); +2914 +2915 // then highlight the ones with matches. +2916 for (var id in matches) { +2917 var factor = 0.5 + matches[id] * 0.2; +2918 this.selector.find("#"+id).dequeue(); +2919 this.selector.find("#"+id).animate({opacity:factor}, 200); +2920 } +2921 +2922 +2923 this.oldSearchMatches = matches; +2924 }; +2925 +2926 IriSP.SegmentsWidget.prototype.searchFieldClearedHandler = function() { +2927 this.clear(); +2928 }; +2929 +2930 IriSP.SegmentsWidget.prototype.searchFieldClosedHandler = function() { +2931 this.clear(); +2932 }; +2933 +2934 IriSP.SegmentsWidget.prototype.positionUpdater = function() { +2935 var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000; +2936 var time = this._Popcorn.currentTime(); +2937 //var position = ((time / duration) * 100).toFixed(2); +2938 var position = ((time / duration) * 100).toFixed(2); +2939 +2940 this.positionMarker.css("left", position + "%"); +2941 }; +2942 IriSP.SliderWidget = function(Popcorn, config, Serializer) { +2943 IriSP.Widget.call(this, Popcorn, config, Serializer); +2944 }; +2945 +2946 IriSP.SliderWidget.prototype = new IriSP.Widget(); +2947 +2948 IriSP.SliderWidget.prototype.draw = function() { +2949 var self = this; +2950 +2951 this.selector.append(Mustache.to_html(IriSP.sliderWidget_template, {})); +2952 this.selector.addClass("Ldt-SliderMinimized"); +2953 +2954 this.sliderBackground = this.selector.find(".Ldt-sliderBackground"); +2955 this.sliderForeground = this.selector.find(".Ldt-sliderForeground"); +2956 this.positionMarker = this.selector.find(".Ldt-sliderPositionMarker"); +2957 +2958 +2959 // a special variable to stop methods from tinkering +2960 // with the positionMarker when the user is dragging it +2961 this.draggingOngoing = false; +2962 +2963 // another special variable used by the timeout handler to +2964 // open or close the slider. +2965 this.sliderMaximized = false; +2966 this.timeOutId = null; +2967 +2968 +2969 this.positionMarker.draggable({axis: "x", +2970 start: IriSP.wrap(this, this.positionMarkerDraggingStartedHandler), +2971 stop: IriSP.wrap(this, this.positionMarkerDraggedHandler), +2972 containment: "parent" +2973 }); +2974 this.positionMarker.css("position", "absolute"); +2975 +2976 this.sliderBackground.click(function(event) { self.backgroundClickHandler.call(self, event); }); +2977 this.sliderForeground.click(function(event) { self.foregroundClickHandler.call(self, event); }); +2978 +2979 this.selector.hover(IriSP.wrap(this, this.mouseOverHandler), IriSP.wrap(this, this.mouseOutHandler)); +2980 +2981 // update the positions +2982 this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater)); +2983 +2984 // special messages : +2985 this._Popcorn.listen("IriSP.PlayerWidget.MouseOver", IriSP.wrap(this, this.mouseOverHandler)); +2986 this._Popcorn.listen("IriSP.PlayerWidget.MouseOut", IriSP.wrap(this, this.mouseOutHandler)); +2987 }; +2988 +2989 /* update the slider and the position marker as time passes */ +2990 IriSP.SliderWidget.prototype.sliderUpdater = function() { +2991 if(this.draggingOngoing || this._disableUpdate) +2992 return; +2993 +2994 var time = this._Popcorn.currentTime(); +2995 +2996 var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000; +2997 var percent = ((time / duration) * 100).toFixed(2); +2998 +2999 /* we do these complicated calculations to center exactly +3000 the position Marker */ +3001 var pixels_to_percents = 100 / this.selector.width(); /* how much is a pixel in percents */ +3002 var positionMarker_width = this.positionMarker.width(); +3003 var correction = (pixels_to_percents * positionMarker_width) / 2; +3004 +3005 var newPos = percent - correction; +3006 if (newPos <= 0) +3007 newPos = 0; +3008 +3009 this.sliderForeground.css("width", percent + "%"); +3010 this.positionMarker.css("left", newPos + "%"); +3011 +3012 }; +3013 +3014 IriSP.SliderWidget.prototype.backgroundClickHandler = function(event) { +3015 /* this piece of code is a little bit convoluted - here's how it works : +3016 we want to handle clicks on the progress bar and convert those to seeks in the media. +3017 However, jquery only gives us a global position, and we want a number of pixels relative +3018 to our container div, so we get the parent position, and compute an offset to this position, +3019 and finally compute the progress ratio in the media. +3020 Finally we multiply this ratio with the duration to get the correct time +3021 */ +3022 +3023 var parentOffset = this.sliderBackground.parent().offset(); +3024 var width = this.sliderBackground.width(); +3025 var relX = event.pageX - parentOffset.left; +3026 +3027 var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000; +3028 var newTime = ((relX / width) * duration).toFixed(2); +3029 +3030 this._Popcorn.currentTime(newTime); +3031 }; +3032 +3033 /* same function as the previous one, except that it handles clicks +3034 on the foreground element */ +3035 IriSP.SliderWidget.prototype.foregroundClickHandler = function(event) { +3036 var parentOffset = this.sliderForeground.parent().offset(); +3037 var width = this.sliderBackground.width(); +3038 var relX = event.pageX - parentOffset.left; +3039 +3040 var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000; +3041 var newTime = ((relX / width) * duration).toFixed(2); +3042 +3043 this._Popcorn.currentTime(newTime); +3044 }; +3045 +3046 /* handles mouse over the slider */ +3047 IriSP.SliderWidget.prototype.mouseOverHandler = function(event) { +3048 +3049 if (this.timeOutId !== null) { +3050 window.clearTimeout(this.timeOutId); +3051 } +3052 +3053 this.sliderMaximized = true; +3054 +3055 this.sliderBackground.animate({"height": "9px"}, 100); +3056 this.sliderForeground.animate({"height": "9px"}, 100); +3057 this.positionMarker.animate({"height": "9px", "width": "9px"}, 100); +3058 //this.positionMarker.css("margin-top", "-4px"); +3059 +3060 // this.selector.removeClass("Ldt-SliderMinimized"); +3061 // this.selector.addClass("Ldt-SliderMaximized"); +3062 }; +3063 +3064 /* handles when the mouse leaves the slider */ +3065 IriSP.SliderWidget.prototype.mouseOutHandler = function(event) { +3066 +3067 this.timeOutId = window.setTimeout(IriSP.wrap(this, this.minimizeOnTimeout), +3068 IriSP.widgetsDefaults.SliderWidget.minimize_period); +3069 }; +3070 +3071 IriSP.SliderWidget.prototype.minimizeOnTimeout = function(event) { +3072 this.sliderBackground.animate({"height": "5px"}, 100); +3073 this.sliderForeground.animate({"height": "5px"}, 100); +3074 this.positionMarker.animate({"height": "5px", "width": "5px"}, 100); +3075 this.positionMarker.css("margin-top", "0px"); +3076 this.sliderMinimized = true; +3077 +3078 // this.selector.removeClass("Ldt-SliderMaximized"); +3079 // this.selector.addClass("Ldt-SliderMinimized"); +3080 +3081 }; +3082 +3083 // called when the user starts dragging the position indicator +3084 IriSP.SliderWidget.prototype.positionMarkerDraggingStartedHandler = function(event, ui) { +3085 this.draggingOngoing = true; +3086 }; +3087 +3088 IriSP.SliderWidget.prototype.positionMarkerDraggedHandler = function(event, ui) { +3089 this._disableUpdate = true; // disable slider position updates while dragging is ongoing. +3090 window.setTimeout(IriSP.wrap(this, function() { this._disableUpdate = false; }), 500); +3091 +3092 var parentOffset = this.sliderForeground.parent().offset(); +3093 var width = this.sliderBackground.width(); +3094 var relX = event.pageX - parentOffset.left; +3095 +3096 var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000; +3097 var newTime = ((relX / width) * duration).toFixed(2); +3098 +3099 this._Popcorn.currentTime(newTime); +3100 +3101 this.draggingOngoing = false; +3102 }; +3103 +3104 /* this widget displays a small tooltip */ +3105 IriSP.TooltipWidget = function(Popcorn, config, Serializer) { +3106 IriSP.Widget.call(this, Popcorn, config, Serializer); +3107 this._shown = false; +3108 this._displayedText = ""; +3109 this._hideTimeout = -1; +3110 }; +3111 +3112 +3113 IriSP.TooltipWidget.prototype = new IriSP.Widget(); +3114 +3115 IriSP.TooltipWidget.prototype.draw = function() { +3116 var templ = Mustache.to_html(IriSP.tooltipWidget_template); +3117 +3118 this.selector.append(templ); +3119 this.hide(); +3120 +3121 }; +3122 +3123 IriSP.TooltipWidget.prototype.clear = function() { +3124 this.selector.find(".tiptext").text(""); +3125 }; +3126 +3127 IriSP.TooltipWidget.prototype.show = function(text, color, x, y) { +3128 +3129 if (this._displayedText == text) +3130 return; +3131 +3132 this.selector.find(".tipcolor").css("background-color", color); +3133 this._displayedText = text; +3134 this.selector.find(".tiptext").text(text); +3135 //this.selector.find(".tip").css("left", x).css("top", y); +3136 this.selector.find(".tip").css("left", x).css("top", y); +3137 this.selector.find(".tip").show(); +3138 this._shown = true; +3139 }; +3140 +3141 IriSP.TooltipWidget.prototype.hide = function() { +3142 this.selector.find(".tip").hide(); +3143 this._shown = false; +3144 };/* a widget that displays tweet - used in conjunction with the polemicWidget */ +3145 +3146 IriSP.TweetsWidget = function(Popcorn, config, Serializer) { +3147 IriSP.Widget.call(this, Popcorn, config, Serializer); +3148 +3149 this._displayingTweet = false; +3150 this._timeoutId = undefined; +3151 }; +3152 +3153 +3154 IriSP.TweetsWidget.prototype = new IriSP.Widget(); +3155 +3156 +3157 IriSP.TweetsWidget.prototype.drawTweet = function(annotation) { +3158 +3159 var title = IriSP.formatTweet(annotation.content.title); +3160 var img = annotation.content.img.src; +3161 if (typeof(img) === "undefined" || img === "" || img === "None") { +3162 img = IriSP.widgetsDefaults.TweetsWidget.default_profile_picture; +3163 } +3164 +3165 var imageMarkup = IriSP.templToHTML("<img src='{{src}}' alt='user image'></img>", +3166 {src : img}); +3167 +3168 if (typeof(annotation.meta["dc:source"].content) !== "undefined") { +3169 var tweetContents = JSON.parse(annotation.meta["dc:source"].content); +3170 var creator = tweetContents.user.screen_name; +3171 var real_name = tweetContents.user.name; +3172 +3173 imageMarkup = IriSP.templToHTML("<a href='http://twitter.com/{{creator}}'><img src='{{src}}' alt='user image'></img></a>", +3174 {src : img, creator: creator}); +3175 +3176 var formatted_date = new Date(tweetContents.created_at).toLocaleDateString(); +3177 title = IriSP.templToHTML("<a class='Ldt-tweet_userHandle' href='http://twitter.com/{{creator}}'>@{{creator}}</a> - " + +3178 "<div class='Ldt-tweet_realName'>{{real_name}}</div>" + +3179 "<div class='Ldt-tweet_tweetContents'>{{{ contents }}}</div>" + +3180 "<div class='Ldt-tweet_date'>{{ date }}</div>", +3181 {creator: creator, real_name: real_name, contents : title, date : formatted_date}); +3182 +3183 this.selector.find(".Ldt-TweetReply").attr("href", "http://twitter.com/home?status=@" + creator + ":%20"); +3184 +3185 +3186 var rtText = Mustache.to_html("http://twitter.com/home?status=RT @{{creator}}: {{text}}", +3187 {creator: creator, text: IriSP.encodeURI(annotation.content.title)}); +3188 this.selector.find(".Ldt-Retweet").attr("href", rtText); +3189 } +3190 +3191 this.selector.find(".Ldt-tweetContents").html(title); +3192 this.selector.find(".Ldt-tweetAvatar").html(imageMarkup); +3193 this.selector.show("blind", 250); +3194 }; +3195 +3196 IriSP.TweetsWidget.prototype.displayTweet = function(annotation) { +3197 if (this._displayingTweet === false) { +3198 this._displayingTweet = true; +3199 } else { +3200 window.clearTimeout(this._timeoutId); +3201 } +3202 +3203 this.drawTweet(annotation); +3204 +3205 var time = this._Popcorn.currentTime(); +3206 this._timeoutId = window.setTimeout(IriSP.wrap(this, this.clearPanel), IriSP.widgetsDefaults.TweetsWidget.tweet_display_period); +3207 }; +3208 +3209 +3210 IriSP.TweetsWidget.prototype.clearPanel = function() { +3211 this._displayingTweet = false; +3212 this._timeoutId = undefined; +3213 this.closePanel(); +3214 +3215 }; +3216 +3217 IriSP.TweetsWidget.prototype.closePanel = function() { +3218 if (this._timeoutId != undefined) { +3219 /* we're called from the "close window" link */ +3220 /* cancel the timeout */ +3221 window.clearTimeout(this._timeoutId); +3222 this._timeoutId = null; +3223 } +3224 +3225 this.selector.hide("blind", 400); +3226 +3227 }; +3228 +3229 /* cancel the timeout if the user clicks on the keep panel open button */ +3230 IriSP.TweetsWidget.prototype.keepPanel = function() { +3231 if (this._timeoutId != undefined) { +3232 /* we're called from the "close window" link */ +3233 /* cancel the timeout */ +3234 window.clearTimeout(this._timeoutId); +3235 this._timeoutId = null; +3236 } +3237 }; +3238 +3239 IriSP.TweetsWidget.prototype.draw = function() { +3240 var _this = this; +3241 +3242 var tweetMarkup = IriSP.templToHTML(IriSP.tweetWidget_template, {"share_template" : IriSP.share_template}); +3243 this.selector.append(tweetMarkup); +3244 this.selector.hide(); +3245 this.selector.find(".Ldt-tweetWidgetMinimize").click(IriSP.wrap(this, this.closePanel)); +3246 this.selector.find(".Ldt-tweetWidgetKeepOpen").click(IriSP.wrap(this, this.keepPanel)); +3247 +3248 this._Popcorn.listen("IriSP.PolemicTweet.click", IriSP.wrap(this, this.PolemicTweetClickHandler)); +3249 }; +3250 +3251 IriSP.TweetsWidget.prototype.PolemicTweetClickHandler = function(tweet_id) { +3252 var index, annotation; +3253 for (index in this._serializer._data.annotations) { +3254 annotation = this._serializer._data.annotations[index]; +3255 +3256 if (annotation.id === tweet_id) +3257 break; +3258 } +3259 +3260 if (annotation.id !== tweet_id) +3261 /* we haven't found it */ +3262 return; +3263 +3264 this.displayTweet(annotation); +3265 return; +3266 }; +3267 +3268 IriSP.JSONSerializer = function(DataLoader, url) { +3269 IriSP.Serializer.call(this, DataLoader, url); +3270 }; +3271 +3272 IriSP.JSONSerializer.prototype = new IriSP.Serializer(); +3273 +3274 IriSP.JSONSerializer.prototype.serialize = function(data) { +3275 return JSON.stringify(data); +3276 }; +3277 +3278 IriSP.JSONSerializer.prototype.deserialize = function(data) { +3279 return JSON.parse(data); +3280 }; +3281 +3282 IriSP.JSONSerializer.prototype.sync = function(callback) { +3283 /* we don't have to do much because jQuery handles json for us */ +3284 +3285 var self = this; +3286 +3287 var fn = function(data) { +3288 self._data = data; +3289 // sort the data too +3290 self._data["annotations"].sort(function(a, b) +3291 { var a_begin = +a.begin; +3292 var b_begin = +b.begin; +3293 return a_begin - b_begin; +3294 }); +3295 +3296 callback(data); +3297 }; +3298 +3299 this._DataLoader.get(this._url, fn); +3300 }; +3301 +3302 IriSP.JSONSerializer.prototype.currentMedia = function() { +3303 return this._data.medias[0]; /* FIXME: don't hardcode it */ +3304 }; +3305 +3306 /* this function searches for an annotation which matches title, description and keyword +3307 "" matches any field. +3308 Note: it ignores tweets. +3309 */ +3310 IriSP.JSONSerializer.prototype.searchAnnotations = function(title, description, keyword) { +3311 /* we can have many types of annotations. We want search to only look for regular segments */ +3312 /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either +3313 null or undefined. +3314 */ +3315 var view; +3316 +3317 if (typeof(this._data.views) !== "undefined" && this._data.views !== null) +3318 view = this._data.views[0]; +3319 +3320 var searchViewType = ""; +3321 +3322 if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) { +3323 searchViewType = view.annotation_types[0]; +3324 } +3325 +3326 var filterfn = function(annotation) { +3327 if( searchViewType != "" && +3328 typeof(annotation.meta) !== "undefined" && +3329 typeof(annotation.meta["id-ref"]) !== "undefined" && +3330 annotation.meta["id-ref"] !== searchViewType) { +3331 return true; // don't pass +3332 } else { +3333 return false; +3334 } +3335 }; +3336 +3337 return this.searchAnnotationsFilter(title, description, keyword, filterfn); +3338 +3339 }; +3340 +3341 /* only look for tweets */ +3342 IriSP.JSONSerializer.prototype.searchTweets = function(title, description, keyword) { +3343 /* we can have many types of annotations. We want search to only look for regular segments */ +3344 /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either +3345 null or undefined. +3346 */ +3347 var view; +3348 +3349 if (typeof(this._data.views) !== "undefined" && this._data.views !== null) +3350 view = this._data.views[0]; +3351 +3352 var searchViewType = ""; +3353 +3354 if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) { +3355 searchViewType = view.annotation_types[0]; +3356 } +3357 +3358 var filterfn = function(annotation) { +3359 if( searchViewType != "" && +3360 typeof(annotation.meta) !== "undefined" && +3361 typeof(annotation.meta["id-ref"]) !== "undefined" && +3362 annotation.meta["id-ref"] !== searchViewType) { +3363 return false; // pass +3364 } else { +3365 return true; +3366 } +3367 }; +3368 +3369 return this.searchAnnotationsFilter(title, description, keyword, filterfn); +3370 +3371 }; +3372 +3373 /* +3374 the previous function call this one, which is more general: +3375 */ +3376 IriSP.JSONSerializer.prototype.searchAnnotationsFilter = function(title, description, keyword, filter) { +3377 +3378 var rTitle; +3379 var rDescription; +3380 var rKeyword; +3381 /* match anything if given the empty string */ +3382 if (title == "") +3383 title = ".*"; +3384 if (description == "") +3385 description = ".*"; +3386 if (keyword == "") +3387 keyword = ".*"; +3388 +3389 rTitle = new RegExp(title, "i"); +3390 rDescription = new RegExp(description, "i"); +3391 rKeyword = new RegExp(keyword, "i"); +3392 +3393 var ret_array = []; +3394 +3395 var i; +3396 for (i in this._data.annotations) { +3397 var annotation = this._data.annotations[i]; +3398 +3399 /* filter the annotations whose type is not the one we want */ +3400 if (filter(annotation)) { +3401 continue; +3402 } +3403 +3404 if (rTitle.test(annotation.content.title) && +3405 rDescription.test(annotation.content.description)) { +3406 /* FIXME : implement keyword support */ +3407 ret_array.push(annotation); +3408 } +3409 } +3410 +3411 return ret_array; +3412 }; +3413 +3414 /* breaks a string in words and searches each of these words. Returns an array +3415 of objects with the id of the annotation and its number of occurences. +3416 +3417 FIXME: optimize ? seems to be n^2 in the worst case. +3418 */ +3419 IriSP.JSONSerializer.prototype.searchOccurences = function(searchString) { +3420 var ret = { }; +3421 var keywords = searchString.split(/\s+/); +3422 +3423 for (var i in keywords) { +3424 var keyword = keywords[i]; +3425 +3426 // search this keyword in descriptions and title +3427 var found_annotations = [] +3428 found_annotations = found_annotations.concat(this.searchAnnotations(keyword, "", "")); +3429 found_annotations = found_annotations.concat(this.searchAnnotations("", keyword, "")); +3430 +3431 for (var j in found_annotations) { +3432 var current_annotation = found_annotations[j]; +3433 +3434 if (!ret.hasOwnProperty(current_annotation.id)) { +3435 ret[current_annotation.id] = 1; +3436 } else { +3437 ret[current_annotation.id] += 1; +3438 } +3439 +3440 } +3441 +3442 }; +3443 +3444 return ret; +3445 }; +3446 +3447 /* breaks a string in words and searches each of these words. Returns an array +3448 of objects with the id of the annotation and its number of occurences. +3449 +3450 FIXME: optimize ? seems to be n^2 in the worst case. +3451 */ +3452 IriSP.JSONSerializer.prototype.searchTweetsOccurences = function(searchString) { +3453 var ret = { }; +3454 var keywords = searchString.split(/\s+/); +3455 +3456 for (var i in keywords) { +3457 var keyword = keywords[i]; +3458 +3459 // search this keyword in descriptions and title +3460 var found_annotations = [] +3461 found_annotations = found_annotations.concat(this.searchTweets(keyword, "", "")); +3462 found_annotations = found_annotations.concat(this.searchTweets("", keyword, "")); +3463 +3464 for (var j in found_annotations) { +3465 var current_annotation = found_annotations[j]; +3466 +3467 if (!ret.hasOwnProperty(current_annotation.id)) { +3468 ret[current_annotation.id] = 1; +3469 } else { +3470 ret[current_annotation.id] += 1; +3471 } +3472 +3473 } +3474 +3475 }; +3476 +3477 return ret; +3478 }; +3479 +3480 /* takes the currentTime and returns all the annotations that are displayable at the moment +3481 NB: only takes account the first type of annotations - ignores tweets +3482 currentTime is in seconds. +3483 */ +3484 +3485 IriSP.JSONSerializer.prototype.currentAnnotations = function(currentTime) { +3486 var view; +3487 var currentTimeMs = 1000 * currentTime; +3488 +3489 if (typeof(this._data.views) !== "undefined" && this._data.views !== null) +3490 view = this._data.views[0]; +3491 +3492 var view_type = ""; +3493 +3494 if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length >= 1) { +3495 view_type = view.annotation_types[0]; +3496 } +3497 +3498 var ret_array = []; +3499 +3500 var i; +3501 +3502 for (i in this._data.annotations) { +3503 var annotation = this._data.annotations[i]; +3504 +3505 if (annotation.meta["id-ref"] === view_type && annotation.begin <= currentTimeMs && annotation.end >= currentTimeMs) +3506 ret_array.push(annotation); +3507 } +3508 +3509 return ret_array; +3510 }; +3511 +3512 +3513 /* this function returns a list of ids of tweet lines */ +3514 IriSP.JSONSerializer.prototype.getTweetIds = function() { +3515 if (typeof(this._data.lists) === "undefined" || this._data.lists === null) +3516 return []; +3517 +3518 var tweetsId = []; +3519 +3520 /* first get the list containing the tweets */ +3521 var tweets = IriSP.underscore.filter(this._data.lists, function(entry) { return entry.id.indexOf("tweet") !== -1 }); +3522 +3523 // FIXME: collect tweets from multiple sources ? +3524 tweetsId = IriSP.underscore.pluck(tweets[0].items, "id-ref"); +3525 +3526 return tweetsId; +3527 }; +3528 +3529 /* this function returns a list of lines which are not tweet lines */ +3530 IriSP.JSONSerializer.prototype.getNonTweetIds = function() { +3531 if (typeof(this._data.lists) === "undefined" || this._data.lists === null) +3532 return []; +3533 +3534 /* get all the ids */ +3535 var ids = IriSP.underscore.map(this._data.lists, function(entry) { +3536 return IriSP.underscore.pluck(entry.items, "id-ref"); }); +3537 +3538 var illegal_values = this.getTweetIds(); +3539 return IriSP.underscore.difference(ids, illegal_values); +3540 +3541 }; +3542\ No newline at end of file diff -r 7b55777486c3 -r 75ba66457232 sbin/res/jsdoc/templates/jsdoc/allclasses.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/res/jsdoc/templates/jsdoc/allclasses.tmpl Fri Apr 06 16:55:34 2012 +0200 @@ -0,0 +1,17 @@ +
+
Version
+ {+ data.version +}.
+
Extends
+ {+
+ data.augments
+ .sort()
+ .map(
+ function($) { return new Link().toSymbol($); }
+ )
+ .join(", ")
+ +}.
+
Defined in: {+new Link().toSrc(data.srcFile)+}.
+
| Constructor Attributes | +Constructor Name and Description | +
|---|---|
| {! + if (data.isPrivate) output += "<private> "; + if (data.isInner) output += "<inner> "; + !} | +
+
+ {+ new Link().toSymbol(data.alias).inner('constructor')+}
+ {+resolveLinks(summarize(data.desc))+}
+ |
+
| Field Attributes | +Field Name and Description | +
|---|---|
| {! + if (member.isPrivate) output += "<private> "; + if (member.isInner) output += "<inner> "; + if (member.isStatic) output += "<static> "; + if (member.isConstant) output += "<constant> "; + !} | +
+
+
+ {+resolveLinks(summarize(member.desc))+}
+ |
+
| Method Attributes | +Method Name and Description | +
|---|---|
| {! + if (member.isPrivate) output += "<private> "; + if (member.isInner) output += "<inner> "; + if (member.isStatic) output += "<static> "; + !} | +
+ {+resolveLinks(summarize(member.desc))+}
+ |
+
| Event Attributes | +Event Name and Description | +
|---|---|
| {! + if (member.isPrivate) output += "<private> "; + if (member.isInner) output += "<inner> "; + if (member.isStatic) output += "<static> "; + !} | +
+ {+resolveLinks(summarize(member.desc))+}
+ |
+
{+example+}
+ {+example+}
+ {+example+}
+ {+example+}
+ Problem' + (isFinite(warning.line) + ? ' at line ' + String(warning.line) + + ' character ' + String(warning.character) + : '') + + ': ' + warning.reason.entityify() + + '
' + + (evidence && (evidence.length > 80 + ? evidence.slice(0, 77) + '...' + : evidence).entityify()) + '
'); + } + } + } + + if (data['undefined']) { + snippets = []; + for (i = 0; i < data['undefined'].length; i += 1) { + snippets[i] = '' + data['undefined'][i].name + ' ' +
+ String(data['undefined'][i].line) + ' ' +
+ data['undefined'][i]['function'] + '';
+ }
+ output.push('Undefined variable: ' + snippets.join(', ') + '
'); + } + if (data.unused) { + snippets = []; + for (i = 0; i < data.unused.length; i += 1) { + snippets[i] = '' + data.unused[i].name + ' ' +
+ String(data.unused[i].line) + ' ' +
+ data.unused[i]['function'] + '';
+ }
+ output.push('Unused variable: ' + snippets.join(', ') + '
'); + } + if (data.json) { + output.push('JSON: bad.
'); + } + output.push('CSS.
'); + } else if (data.json && !err) { + output.push('JSON: good.
'); + } else if (data.globals) { + output.push('/*properties'); + } + output.push('
'); + mem = ' '; + italics = 0; + j = 0; + if (option.confusion) { + for (i = 0; i < keys.length; i += 1) { + key = keys[i]; + if (typeof standard_property_type[key] !== 'string') { + name = ix.test(key) + ? key + : '\'' + key.entityify().replace(nx, sanitize) + '\''; + if (data.member[key] === 1) { + name = '' + name + ''; + italics += 1; + j = 1; + } + if (i < keys.length - 1) { + name += ', '; + } + if (mem.length + name.length - (italics * 7) > 80) { + output.push(mem + '
'); + mem = ' '; + italics = j; + } + mem += name; + j = 0; + } + } + } else { + for (i = 0; i < keys.length; i += 1) { + key = keys[i]; + type = property_type[key]; + if (typeof type !== 'string') { + type = ''; + } + if (standard_property_type[key] !== type) { + name = ix.test(key) + ? key + : '\'' + key.entityify().replace(nx, sanitize) + '\''; + length += name.length + 2; + if (data.member[key] === 1) { + name = '' + name + ''; + italics += 1; + j = 1; + } + if (type) { + name += ': ' + type; + } + if (i < keys.length - 1) { + name += ', '; + } + if (mem.length + name.length - (italics * 7) > 80) { + output.push(mem + '
'); + mem = ' '; + italics = j; + } + mem += name; + j = 0; + } + } + } + output.push(mem + '
*/
| '+this._get(a,"weekHeader")+" | ":"";for(t=0;t<7;t++){var q=(t+h)%7;D+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+" | "}y+=D+"'+this._get(a,"calculateWeek")(q)+" | ";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],F=q.getMonth()!=g,L=F&&!K||!I[0]||k&&q"+(F&&!C?" ":L?''+q.getDate()+ +"":''+q.getDate()+"")+" | ";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}y+=R+""}g++;if(g>11){g=0;m++}y+="
|---|
| t |
| '+this._get(a,"weekHeader")+" | ":"";for(t=0;t<7;t++){var q=(t+h)%7;D+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+" | "}y+=D+"'+this._get(a,"calculateWeek")(q)+" | ";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],F=q.getMonth()!=g,L=F&&!K||!I[0]||k&&q"+(F&&!C?" ":L?''+q.getDate()+ +"":''+q.getDate()+"")+" | ";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}y+=R+""}g++;if(g>11){g=0;m++}y+="
|---|
| t |