client/js/main.js
changeset 444 19f0b7803aed
parent 433 e457ec945e50
child 455 18b9be54174d
child 459 98cae534083d
equal deleted inserted replaced
443:4c7ab16e5845 444:19f0b7803aed
     1 
       
     2 /* Declaring the Renkan Namespace Rkns and Default values */
     1 /* Declaring the Renkan Namespace Rkns and Default values */
     3 
     2 
     4 (function(root) {
     3 (function(root) {
     5 
     4 
     6 "use strict";
     5     "use strict";
     7 
     6 
     8 if (typeof root.Rkns !== "object") {
     7     if (typeof root.Rkns !== "object") {
     9     root.Rkns = {};
     8         root.Rkns = {};
    10 }
     9     }
    11 
    10 
    12 var Rkns = root.Rkns;
    11     var Rkns = root.Rkns;
    13 var $ = Rkns.$ = root.jQuery;
    12     var $ = Rkns.$ = root.jQuery;
    14 var _ = Rkns._ = root._;
    13     var _ = Rkns._ = root._;
    15 
    14 
    16 Rkns.pickerColors = ["#8f1919", "#a80000", "#d82626", "#ff0000", "#e87c7c", "#ff6565", "#f7d3d3", "#fecccc",
    15     Rkns.pickerColors = ["#8f1919", "#a80000", "#d82626", "#ff0000", "#e87c7c", "#ff6565", "#f7d3d3", "#fecccc",
    17     "#8f5419", "#a85400", "#d87f26", "#ff7f00", "#e8b27c", "#ffb265", "#f7e5d3", "#fee5cc",
    16         "#8f5419", "#a85400", "#d87f26", "#ff7f00", "#e8b27c", "#ffb265", "#f7e5d3", "#fee5cc",
    18     "#8f8f19", "#a8a800", "#d8d826", "#feff00", "#e8e87c", "#feff65", "#f7f7d3", "#fefecc",
    17         "#8f8f19", "#a8a800", "#d8d826", "#feff00", "#e8e87c", "#feff65", "#f7f7d3", "#fefecc",
    19     "#198f19", "#00a800", "#26d826", "#00ff00", "#7ce87c", "#65ff65", "#d3f7d3", "#ccfecc",
    18         "#198f19", "#00a800", "#26d826", "#00ff00", "#7ce87c", "#65ff65", "#d3f7d3", "#ccfecc",
    20     "#198f8f", "#00a8a8", "#26d8d8", "#00feff", "#7ce8e8", "#65feff", "#d3f7f7", "#ccfefe",
    19         "#198f8f", "#00a8a8", "#26d8d8", "#00feff", "#7ce8e8", "#65feff", "#d3f7f7", "#ccfefe",
    21     "#19198f", "#0000a8", "#2626d8", "#0000ff", "#7c7ce8", "#6565ff", "#d3d3f7", "#ccccfe",
    20         "#19198f", "#0000a8", "#2626d8", "#0000ff", "#7c7ce8", "#6565ff", "#d3d3f7", "#ccccfe",
    22     "#8f198f", "#a800a8", "#d826d8", "#ff00fe", "#e87ce8", "#ff65fe", "#f7d3f7", "#feccfe",
    21         "#8f198f", "#a800a8", "#d826d8", "#ff00fe", "#e87ce8", "#ff65fe", "#f7d3f7", "#feccfe",
    23     "#000000", "#242424", "#484848", "#6d6d6d", "#919191", "#b6b6b6", "#dadada", "#ffffff"];
    22         "#000000", "#242424", "#484848", "#6d6d6d", "#919191", "#b6b6b6", "#dadada", "#ffffff"
    24 
    23     ];
    25 Rkns.__renkans = [];
    24 
    26 
    25     Rkns.__renkans = [];
    27 var _BaseBin = Rkns._BaseBin = function(_renkan, _opts) {
    26 
    28     if (typeof _renkan !== "undefined") {
    27     var _BaseBin = Rkns._BaseBin = function(_renkan, _opts) {
    29         this.renkan = _renkan;
    28         if (typeof _renkan !== "undefined") {
    30         this.renkan.$.find(".Rk-Bin-Main").hide();
    29             this.renkan = _renkan;
    31         this.$ = Rkns.$('<li>')
    30             this.renkan.$.find(".Rk-Bin-Main").hide();
    32             .addClass("Rk-Bin")
    31             this.$ = Rkns.$('<li>')
    33             .appendTo(_renkan.$.find(".Rk-Bin-List"));
    32                 .addClass("Rk-Bin")
    34         this.title_icon_$ = Rkns.$('<span>')
    33                 .appendTo(_renkan.$.find(".Rk-Bin-List"));
    35             .addClass("Rk-Bin-Title-Icon")
    34             this.title_icon_$ = Rkns.$('<span>')
    36             .appendTo(this.$);
    35                 .addClass("Rk-Bin-Title-Icon")
    37 
    36                 .appendTo(this.$);
       
    37 
       
    38             var _this = this;
       
    39 
       
    40             Rkns.$('<a>')
       
    41                 .attr({
       
    42                     href: "#",
       
    43                     title: _renkan.translate("Close bin")
       
    44                 })
       
    45                 .addClass("Rk-Bin-Close")
       
    46                 .html('&times;')
       
    47                 .appendTo(this.$)
       
    48                 .click(function() {
       
    49                     _this.destroy();
       
    50                     if (!_renkan.$.find(".Rk-Bin-Main:visible").length) {
       
    51                         _renkan.$.find(".Rk-Bin-Main:last").slideDown();
       
    52                     }
       
    53                     _renkan.resizeBins();
       
    54                     return false;
       
    55                 });
       
    56             Rkns.$('<a>')
       
    57                 .attr({
       
    58                     href: "#",
       
    59                     title: _renkan.translate("Refresh bin")
       
    60                 })
       
    61                 .addClass("Rk-Bin-Refresh")
       
    62                 .appendTo(this.$)
       
    63                 .click(function() {
       
    64                     _this.refresh();
       
    65                     return false;
       
    66                 });
       
    67             this.count_$ = Rkns.$('<div>')
       
    68                 .addClass("Rk-Bin-Count")
       
    69                 .appendTo(this.$);
       
    70             this.title_$ = Rkns.$('<h2>')
       
    71                 .addClass("Rk-Bin-Title")
       
    72                 .appendTo(this.$);
       
    73             this.main_$ = Rkns.$('<div>')
       
    74                 .addClass("Rk-Bin-Main")
       
    75                 .appendTo(this.$)
       
    76                 .html('<h4 class="Rk-Bin-Loading">' + _renkan.translate("Loading, please wait") + '</h4>');
       
    77             this.title_$.html(_opts.title || '(new bin)');
       
    78             this.renkan.resizeBins();
       
    79 
       
    80             if (_opts.auto_refresh) {
       
    81                 window.setInterval(function() {
       
    82                     _this.refresh();
       
    83                 }, _opts.auto_refresh);
       
    84             }
       
    85         }
       
    86     };
       
    87 
       
    88     _BaseBin.prototype.destroy = function() {
       
    89         this.$.detach();
       
    90         this.renkan.resizeBins();
       
    91     };
       
    92 
       
    93     /* Point of entry */
       
    94 
       
    95     var Renkan = Rkns.Renkan = function(_opts) {
    38         var _this = this;
    96         var _this = this;
    39 
    97 
    40         Rkns.$('<a>')
    98         Rkns.__renkans.push(this);
    41             .attr({
    99 
    42                 href: "#",
   100         this.options = _.defaults(_opts, Rkns.defaults, {
    43                 title: _renkan.translate("Close bin")
   101             templates: renkanJST
    44             })
   102         });
    45             .addClass("Rk-Bin-Close")
   103         this.template = renkanJST['templates/main.html'];
    46             .html('&times;')
   104 
    47             .appendTo(this.$)
   105         _.each(this.options.property_files, function(f) {
    48             .click(function() {
   106             Rkns.$.getJSON(f, function(data) {
    49                 _this.destroy();
   107                 _this.options.properties = _this.options.properties.concat(data);
    50                 if (!_renkan.$.find(".Rk-Bin-Main:visible").length) {
   108             });
    51                     _renkan.$.find(".Rk-Bin-Main:last").slideDown();
   109         });
    52                 }
   110 
    53                 _renkan.resizeBins();
   111         this.read_only = this.options.read_only || !this.options.editor_mode;
       
   112 
       
   113         this.project = new Rkns.Models.Project();
       
   114         this.dataloader = new Rkns.DataLoader.Loader(this.project, this.options);
       
   115 
       
   116         this.setCurrentUser = function(user_id, user_name) {
       
   117             this.project.addUser({
       
   118                 _id: user_id,
       
   119                 title: user_name
       
   120             });
       
   121             this.current_user = user_id;
       
   122             this.renderer.redrawUsers();
       
   123         };
       
   124 
       
   125         if (typeof this.options.user_id !== "undefined") {
       
   126             this.current_user = this.options.user_id;
       
   127         }
       
   128         this.$ = Rkns.$("#" + this.options.container);
       
   129         this.$
       
   130             .addClass("Rk-Main")
       
   131             .html(this.template(this));
       
   132 
       
   133         this.tabs = [];
       
   134         this.search_engines = [];
       
   135 
       
   136         this.current_user_list = new Rkns.Models.UsersList();
       
   137 
       
   138         this.current_user_list.on("add remove", function() {
       
   139             if (this.renderer) {
       
   140                 this.renderer.redrawUsers();
       
   141             }
       
   142         });
       
   143 
       
   144         this.colorPicker = (function() {
       
   145             var _tmpl = renkanJST['templates/colorpicker.html'];
       
   146             return '<ul class="Rk-Edit-ColorPicker">' + Rkns.pickerColors.map(function(c) {
       
   147                 return _tmpl({
       
   148                     c: c
       
   149                 });
       
   150             }).join("") + '</ul>';
       
   151         })();
       
   152 
       
   153         if (this.options.show_editor) {
       
   154             this.renderer = new Rkns.Renderer.Scene(this);
       
   155         }
       
   156 
       
   157         if (!this.options.search.length) {
       
   158             this.$.find(".Rk-Web-Search-Form").detach();
       
   159         } else {
       
   160             var _tmpl = renkanJST['templates/search.html'],
       
   161                 _select = this.$.find(".Rk-Search-List"),
       
   162                 _input = this.$.find(".Rk-Web-Search-Input"),
       
   163                 _form = this.$.find(".Rk-Web-Search-Form");
       
   164             _.each(this.options.search, function(_search, _key) {
       
   165                 if (Rkns[_search.type] && Rkns[_search.type].Search) {
       
   166                     _this.search_engines.push(new Rkns[_search.type].Search(_this, _search));
       
   167                 }
       
   168             });
       
   169             _select.html(
       
   170                 _(this.search_engines).map(function(_search, _key) {
       
   171                     return _tmpl({
       
   172                         key: _key,
       
   173                         title: _search.getSearchTitle(),
       
   174                         className: _search.getBgClass()
       
   175                     });
       
   176                 }).join("")
       
   177             );
       
   178             _select.find("li").click(function() {
       
   179                 var _el = Rkns.$(this);
       
   180                 _this.setSearchEngine(_el.attr("data-key"));
       
   181                 _form.submit();
       
   182             });
       
   183             _form.submit(function() {
       
   184                 if (_input.val()) {
       
   185                     var _search = _this.search_engine;
       
   186                     _search.search(_input.val());
       
   187                 }
    54                 return false;
   188                 return false;
    55             });
   189             });
    56         Rkns.$('<a>')
   190             this.$.find(".Rk-Search-Current").mouseenter(
    57             .attr({
   191                 function() {
    58                 href: "#",
   192                     _select.slideDown();
    59                 title: _renkan.translate("Refresh bin")
   193                 }
    60             })
   194             );
    61             .addClass("Rk-Bin-Refresh")
   195             this.$.find(".Rk-Search-Select").mouseleave(
    62             .appendTo(this.$)
   196                 function() {
    63             .click(function() {
   197                     _select.hide();
    64                 _this.refresh();
   198                 }
    65                 return false;
   199             );
    66             });
   200             this.setSearchEngine(0);
    67         this.count_$ = Rkns.$('<div>')
   201         }
    68             .addClass("Rk-Bin-Count")
   202         _.each(this.options.bins, function(_bin) {
    69             .appendTo(this.$);
   203             if (Rkns[_bin.type] && Rkns[_bin.type].Bin) {
    70         this.title_$ = Rkns.$('<h2>')
   204                 _this.tabs.push(new Rkns[_bin.type].Bin(_this, _bin));
    71             .addClass("Rk-Bin-Title")
   205             }
    72             .appendTo(this.$);
   206         });
    73         this.main_$ = Rkns.$('<div>')
   207 
    74             .addClass("Rk-Bin-Main")
   208         var elementDropped = false;
    75             .appendTo(this.$)
   209 
    76             .html('<h4 class="Rk-Bin-Loading">' + _renkan.translate("Loading, please wait") + '</h4>');
   210         this.$.find(".Rk-Bins")
    77         this.title_$.html(_opts.title || '(new bin)');
   211             .on("click", ".Rk-Bin-Title,.Rk-Bin-Title-Icon", function() {
    78         this.renkan.resizeBins();
   212                 var _mainDiv = Rkns.$(this).siblings(".Rk-Bin-Main");
    79 
   213                 if (_mainDiv.is(":hidden")) {
    80         if (_opts.auto_refresh) {
   214                     _this.$.find(".Rk-Bin-Main").slideUp();
    81             window.setInterval(function() {
   215                     _mainDiv.slideDown();
    82                 _this.refresh();
   216                 }
    83             },_opts.auto_refresh);
   217             });
    84         }
   218 
    85     }
   219         if (this.options.show_editor) {
    86 };
   220 
    87 
   221             this.$.find(".Rk-Bins").on("mouseover", ".Rk-Bin-Item", function(_e) {
    88 _BaseBin.prototype.destroy = function() {
   222                 var _t = Rkns.$(this);
    89     this.$.detach();
   223                 if (_t && $(_t).attr("data-uri")) {
    90     this.renkan.resizeBins();
   224                     var _models = _this.project.get("nodes").where({
    91 };
   225                         uri: $(_t).attr("data-uri")
    92 
   226                     });
    93 /* Point of entry */
   227                     _.each(_models, function(_model) {
    94 
   228                         _this.renderer.highlightModel(_model);
    95 var Renkan = Rkns.Renkan = function(_opts) {
   229                     });
    96     var _this = this;
   230                 }
    97 
   231             }).mouseout(function() {
    98     Rkns.__renkans.push(this);
   232                 _this.renderer.unhighlightAll();
    99 
   233             }).on("mousemove", ".Rk-Bin-Item", function(e) {
   100     this.options = _.defaults(_opts, Rkns.defaults, {templates: renkanJST});
   234                 try {
   101     this.template = renkanJST['templates/main.html'];
   235                     this.dragDrop();
   102 
   236                 } catch (err) {}
   103     _.each(this.options.property_files,function(f) {
   237             }).on("touchstart", ".Rk-Bin-Item", function(e) {
   104         Rkns.$.getJSON(f, function(data) {
   238                 elementDropped = false;
   105             _this.options.properties = _this.options.properties.concat(data);
   239             }).on("touchmove", ".Rk-Bin-Item", function(e) {
   106         });
   240                 e.preventDefault();
   107     });
   241                 var touch = e.originalEvent.changedTouches[0],
   108 
   242                     off = _this.renderer.canvas_$.offset(),
   109     this.read_only = this.options.read_only || !this.options.editor_mode;
   243                     w = _this.renderer.canvas_$.width(),
   110 
   244                     h = _this.renderer.canvas_$.height();
   111     this.project = new Rkns.Models.Project();
   245                 if (touch.pageX >= off.left && touch.pageX < (off.left + w) && touch.pageY >= off.top && touch.pageY < (off.top + h)) {
   112 
   246                     if (elementDropped) {
   113     this.setCurrentUser = function (user_id, user_name) {
   247                         _this.renderer.onMouseMove(touch, true);
   114     	this.project.addUser({
   248                     } else {
   115     		_id:user_id,
   249                         elementDropped = true;
   116     		title: user_name
   250                         var div = document.createElement('div');
   117     	});
   251                         div.appendChild(this.cloneNode(true));
   118     	this.current_user = user_id;
   252                         _this.renderer.dropData({
   119     	this.renderer.redrawUsers();
   253                             "text/html": div.innerHTML
   120     };
   254                         }, touch);
   121 
   255                         _this.renderer.onMouseDown(touch, true);
   122     if (typeof this.options.user_id !== "undefined") {
   256                     }
   123         this.current_user = this.options.user_id;
   257                 }
   124     }
   258             }).on("touchend", ".Rk-Bin-Item", function(e) {
   125     this.$ = Rkns.$("#" + this.options.container);
   259                 if (elementDropped) {
   126     this.$
   260                     _this.renderer.onMouseUp(e.originalEvent.changedTouches[0], true);
   127         .addClass("Rk-Main")
   261                 }
   128         .html(this.template(this));
   262                 elementDropped = false;
   129 
   263             }).on("dragstart", ".Rk-Bin-Item", function(e) {
   130     this.tabs = [];
   264                 var div = document.createElement('div');
   131     this.search_engines = [];
   265                 div.appendChild(this.cloneNode(true));
   132 
   266                 try {
   133     this.current_user_list = new Rkns.Models.UsersList();
   267                     e.originalEvent.dataTransfer.setData("text/html", div.innerHTML);
   134 
   268                 } catch (err) {
   135     this.current_user_list.on("add remove", function() {
   269                     e.originalEvent.dataTransfer.setData("text", div.innerHTML);
   136         if (this.renderer) {
   270                 }
   137             this.renderer.redrawUsers();
   271             });
   138         }
   272 
   139     });
   273         }
   140 
   274 
   141     this.colorPicker = (function() {
   275         Rkns.$(window).resize(function() {
   142         var _tmpl = renkanJST['templates/colorpicker.html'];
   276             _this.resizeBins();
   143         return '<ul class="Rk-Edit-ColorPicker">' + Rkns.pickerColors.map(function(c) { return _tmpl({c:c});}).join("") + '</ul>';
   277         });
   144     })();
   278 
   145 
   279         var lastsearch = false,
   146     if (this.options.show_editor) {
   280             lastval = '';
   147         this.renderer = new Rkns.Renderer.Scene(this);
   281 
   148     }
   282         this.$.find(".Rk-Bins-Search-Input").on("change keyup paste input", function() {
   149 
   283             var val = Rkns.$(this).val();
   150     if (!this.options.search.length) {
   284             if (val === lastval) {
   151         this.$.find(".Rk-Web-Search-Form").detach();
   285                 return;
   152     } else {
   286             }
   153         var _tmpl = renkanJST['templates/search.html'],
   287             var search = Rkns.Utils.regexpFromTextOrArray(val.length > 1 ? val : null);
   154             _select = this.$.find(".Rk-Search-List"),
   288             if (search.source === lastsearch) {
   155             _input = this.$.find(".Rk-Web-Search-Input"),
   289                 return;
   156             _form = this.$.find(".Rk-Web-Search-Form");
   290             }
   157         _.each(this.options.search, function(_search, _key) {
   291             lastsearch = search.source;
   158             if (Rkns[_search.type] && Rkns[_search.type].Search) {
   292             _.each(_this.tabs, function(tab) {
   159                 _this.search_engines.push(new Rkns[_search.type].Search(_this, _search));
   293                 tab.render(search);
   160             }
   294             });
   161         });
   295 
   162         _select.html(
   296         });
   163             _(this.search_engines).map(function(_search, _key) {
   297         this.$.find(".Rk-Bins-Search-Form").submit(function() {
   164                 return _tmpl({
   298             return false;
   165                     key: _key,
   299         });
   166                     title: _search.getSearchTitle(),
   300 
   167                     className: _search.getBgClass()
   301     };
       
   302 
       
   303     Renkan.prototype.translate = function(_text) {
       
   304         if (Rkns.i18n[this.options.language] && Rkns.i18n[this.options.language][_text]) {
       
   305             return Rkns.i18n[this.options.language][_text];
       
   306         }
       
   307         if (this.options.language.length > 2 && Rkns.i18n[this.options.language.substr(0, 2)] && Rkns.i18n[this.options.language.substr(0, 2)][_text]) {
       
   308             return Rkns.i18n[this.options.language.substr(0, 2)][_text];
       
   309         }
       
   310         return _text;
       
   311     };
       
   312 
       
   313     Renkan.prototype.onStatusChange = function() {
       
   314         this.renderer.onStatusChange();
       
   315     };
       
   316 
       
   317     Renkan.prototype.setSearchEngine = function(_key) {
       
   318         this.search_engine = this.search_engines[_key];
       
   319         this.$.find(".Rk-Search-Current").attr("class", "Rk-Search-Current " + this.search_engine.getBgClass());
       
   320         var listClasses = this.search_engine.getBgClass().split(" ");
       
   321         var classes = "";
       
   322         for (var i = 0; i < listClasses.length; i++) {
       
   323             classes += "." + listClasses[i];
       
   324         }
       
   325         this.$.find(".Rk-Web-Search-Input.Rk-Search-Input").attr("placeholder", this.translate("Search in ") + this.$.find(".Rk-Search-List " + classes).html());
       
   326     };
       
   327 
       
   328     Renkan.prototype.resizeBins = function() {
       
   329         var _d = +this.$.find(".Rk-Bins-Head").outerHeight();
       
   330         this.$.find(".Rk-Bin-Title:visible").each(function() {
       
   331             _d += Rkns.$(this).outerHeight();
       
   332         });
       
   333         this.$.find(".Rk-Bin-Main").css({
       
   334             height: this.$.find(".Rk-Bins").height() - _d
       
   335         });
       
   336     };
       
   337 
       
   338     /* Utility functions */
       
   339     var getUUID4 = function() {
       
   340         return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
       
   341             var r = Math.random() * 16 | 0,
       
   342                 v = c === 'x' ? r : (r & 0x3 | 0x8);
       
   343             return v.toString(16);
       
   344         });
       
   345     };
       
   346 
       
   347     Rkns.Utils = {
       
   348         getUUID4: getUUID4,
       
   349         getUID: (function() {
       
   350             function pad(n) {
       
   351                 return n < 10 ? '0' + n : n;
       
   352             }
       
   353             var _d = new Date(),
       
   354                 ID_AUTO_INCREMENT = 0,
       
   355                 ID_BASE = _d.getUTCFullYear() + '-' +
       
   356                 pad(_d.getUTCMonth() + 1) + '-' +
       
   357                 pad(_d.getUTCDate()) + '-' +
       
   358                 getUUID4();
       
   359             return function(_base) {
       
   360                 var _n = (++ID_AUTO_INCREMENT).toString(16),
       
   361                     _uidbase = (typeof _base === "undefined" ? "" : _base + "-");
       
   362                 while (_n.length < 4) {
       
   363                     _n = '0' + _n;
       
   364                 }
       
   365                 return _uidbase + ID_BASE + '-' + _n;
       
   366             };
       
   367         })(),
       
   368         getFullURL: function(url) {
       
   369 
       
   370             if (typeof(url) === 'undefined' || url == null) {
       
   371                 return "";
       
   372             }
       
   373             if (/https?:\/\//.test(url)) {
       
   374                 return url;
       
   375             }
       
   376             var img = new Image();
       
   377             img.src = url;
       
   378             var res = img.src;
       
   379             img.src = null;
       
   380             return res;
       
   381 
       
   382         },
       
   383         inherit: function(_baseClass, _callbefore) {
       
   384 
       
   385             var _class = function(_arg) {
       
   386                 if (typeof _callbefore === "function") {
       
   387                     _callbefore.apply(this, Array.prototype.slice.call(arguments, 0));
       
   388                 }
       
   389                 _baseClass.apply(this, Array.prototype.slice.call(arguments, 0));
       
   390                 if (typeof this._init === "function" && !this._initialized) {
       
   391                     this._init.apply(this, Array.prototype.slice.call(arguments, 0));
       
   392                     this._initialized = true;
       
   393                 }
       
   394             };
       
   395             _.extend(_class.prototype, _baseClass.prototype);
       
   396 
       
   397             return _class;
       
   398 
       
   399         },
       
   400         regexpFromTextOrArray: (function() {
       
   401             var charsub = [
       
   402                     '[aáàâä]',
       
   403                     '[cç]',
       
   404                     '[eéèêë]',
       
   405                     '[iíìîï]',
       
   406                     '[oóòôö]',
       
   407                     '[uùûü]'
       
   408                 ],
       
   409                 removeChars = [
       
   410                     String.fromCharCode(768), String.fromCharCode(769), String.fromCharCode(770), String.fromCharCode(771), String.fromCharCode(807),
       
   411                     "{", "}", "(", ")", "[", "]", "【", "】", "、", "・", "‥", "。", "「", "」", "『", "』", "〜", ":", "!", "?", " ",
       
   412                     ",", " ", ";", "(", ")", ".", "*", "+", "\\", "?", "|", "{", "}", "[", "]", "^", "#", "/"
       
   413                 ],
       
   414                 remsrc = "[\\" + removeChars.join("\\") + "]",
       
   415                 remrx = new RegExp(remsrc, "gm"),
       
   416                 charsrx = _.map(charsub, function(c) {
       
   417                     return new RegExp(c);
   168                 });
   418                 });
   169             }).join("")
   419 
   170         );
   420             function replaceText(_text) {
   171         _select.find("li").click(function() {
   421                 var txt = _text.toLowerCase().replace(remrx, ""),
   172             var _el = Rkns.$(this);
   422                     src = "";
   173             _this.setSearchEngine(_el.attr("data-key"));
   423 
   174             _form.submit();
   424                 function makeReplaceFunc(l) {
   175         });
   425                     return function(k, v) {
   176         _form.submit(function() {
   426                         l = l.replace(charsrx[k], v);
   177             if (_input.val()) {
   427                     };
   178                 var _search = _this.search_engine;
   428                 }
   179                 _search.search(_input.val());
   429                 for (var j = 0; j < txt.length; j++) {
   180             }
   430                     if (j) {
   181             return false;
   431                         src += remsrc + "*";
   182         });
   432                     }
   183         this.$.find(".Rk-Search-Current").mouseenter(
   433                     var l = txt[j];
   184             function() { _select.slideDown(); }
   434                     _.each(charsub, makeReplaceFunc(l));
   185         );
   435                     src += l;
   186         this.$.find(".Rk-Search-Select").mouseleave(
   436                 }
   187             function() { _select.hide(); }
   437                 return src;
   188         );
   438             }
   189         this.setSearchEngine(0);
   439 
   190     }
   440             function getSource(inp) {
   191     _.each(this.options.bins, function(_bin) {
   441                 switch (typeof inp) {
   192         if (Rkns[_bin.type] && Rkns[_bin.type].Bin) {
   442                     case "string":
   193             _this.tabs.push(new Rkns[_bin.type].Bin(_this, _bin));
   443                         return replaceText(inp);
   194         }
   444                     case "object":
   195     });
   445                         var src = '';
   196 
   446                         _.each(inp, function(v) {
   197     var elementDropped = false;
   447                             var res = getSource(v);
   198 
   448                             if (res) {
   199     this.$.find(".Rk-Bins")
   449                                 if (src) {
   200         .on("click",".Rk-Bin-Title,.Rk-Bin-Title-Icon", function() {
   450                                     src += '|';
   201             var _mainDiv = Rkns.$(this).siblings(".Rk-Bin-Main");
   451                                 }
   202             if (_mainDiv.is(":hidden")) {
   452                                 src += res;
   203                 _this.$.find(".Rk-Bin-Main").slideUp();
   453                             }
   204                 _mainDiv.slideDown();
   454                         });
   205             }
   455                         return src;
   206         });
   456                 }
   207 
   457                 return '';
   208     if (this.options.show_editor) {
   458             }
   209 
   459 
   210         this.$.find(".Rk-Bins").on("mouseover", ".Rk-Bin-Item", function(_e) {
   460             return function(_textOrArray) {
   211             var _t = Rkns.$(this);
   461                 var source = getSource(_textOrArray);
   212             if (_t && $(_t).attr("data-uri")) {
   462                 if (source) {
   213                 var _models = _this.project.get("nodes").where({
   463                     var testrx = new RegExp(source, "im"),
   214                     uri: $(_t).attr("data-uri")
   464                         replacerx = new RegExp('(' + source + ')', "igm");
   215                 });
   465                     return {
   216                 _.each(_models, function(_model) {
   466                         isempty: false,
   217                     _this.renderer.highlightModel(_model);
   467                         source: source,
   218                 });
   468                         test: function(_t) {
   219             }
   469                             return testrx.test(_t);
   220         }).mouseout(function() {
   470                         },
   221             _this.renderer.unhighlightAll();
   471                         replace: function(_text, _replace) {
   222         }).on("mousemove", ".Rk-Bin-Item", function(e) {
   472                             return _text.replace(replacerx, _replace);
   223             try {
   473                         }
   224                 this.dragDrop();
   474                     };
   225             }
       
   226             catch(err) {}
       
   227         }).on("touchstart", ".Rk-Bin-Item", function(e) {
       
   228             elementDropped = false;
       
   229         }).on("touchmove", ".Rk-Bin-Item", function(e) {
       
   230             e.preventDefault();
       
   231             var touch = e.originalEvent.changedTouches[0],
       
   232                 off = _this.renderer.canvas_$.offset(),
       
   233                 w = _this.renderer.canvas_$.width(),
       
   234                 h = _this.renderer.canvas_$.height();
       
   235             if (touch.pageX >= off.left && touch.pageX < (off.left + w) && touch.pageY >= off.top && touch.pageY < (off.top + h)) {
       
   236                 if (elementDropped) {
       
   237                     _this.renderer.onMouseMove(touch, true);
       
   238                 } else {
   475                 } else {
   239                     elementDropped = true;
   476                     return {
   240                     var div = document.createElement('div');
   477                         isempty: true,
   241                     div.appendChild(this.cloneNode(true));
   478                         source: '',
   242                     _this.renderer.dropData({"text/html": div.innerHTML}, touch);
   479                         test: function() {
   243                     _this.renderer.onMouseDown(touch, true);
   480                             return true;
   244                 }
   481                         },
   245             }
   482                         replace: function(_text) {
   246         }).on("touchend", ".Rk-Bin-Item", function(e) {
   483                             return text;
   247             if (elementDropped) {
       
   248                 _this.renderer.onMouseUp(e.originalEvent.changedTouches[0], true);
       
   249             }
       
   250             elementDropped = false;
       
   251         }).on("dragstart", ".Rk-Bin-Item", function(e) {
       
   252             var div = document.createElement('div');
       
   253             div.appendChild(this.cloneNode(true));
       
   254             try {
       
   255                 e.originalEvent.dataTransfer.setData("text/html",div.innerHTML);
       
   256             }
       
   257             catch(err) {
       
   258                 e.originalEvent.dataTransfer.setData("text",div.innerHTML);
       
   259             }
       
   260         });
       
   261 
       
   262     }
       
   263 
       
   264     Rkns.$(window).resize(function() {
       
   265         _this.resizeBins();
       
   266     });
       
   267 
       
   268     var lastsearch = false, lastval = '';
       
   269 
       
   270     this.$.find(".Rk-Bins-Search-Input").on("change keyup paste input", function() {
       
   271         var val = Rkns.$(this).val();
       
   272         if (val === lastval) {
       
   273             return;
       
   274         }
       
   275         var search = Rkns.Utils.regexpFromTextOrArray(val.length > 1 ? val: null);
       
   276         if (search.source === lastsearch) {
       
   277             return;
       
   278         }
       
   279         lastsearch = search.source;
       
   280         _.each(_this.tabs, function(tab) {
       
   281             tab.render(search);
       
   282         });
       
   283 
       
   284     });
       
   285     this.$.find(".Rk-Bins-Search-Form").submit(function() {
       
   286         return false;
       
   287     });
       
   288 
       
   289 };
       
   290 
       
   291 Renkan.prototype.translate = function(_text) {
       
   292     if (Rkns.i18n[this.options.language] && Rkns.i18n[this.options.language][_text]) {
       
   293         return Rkns.i18n[this.options.language][_text];
       
   294     }
       
   295     if (this.options.language.length > 2 && Rkns.i18n[this.options.language.substr(0,2)] && Rkns.i18n[this.options.language.substr(0,2)][_text]) {
       
   296         return Rkns.i18n[this.options.language.substr(0,2)][_text];
       
   297     }
       
   298     return _text;
       
   299 };
       
   300 
       
   301 Renkan.prototype.onStatusChange = function() {
       
   302     this.renderer.onStatusChange();
       
   303 };
       
   304 
       
   305 Renkan.prototype.setSearchEngine = function(_key) {
       
   306     this.search_engine = this.search_engines[_key];
       
   307     this.$.find(".Rk-Search-Current").attr("class","Rk-Search-Current " + this.search_engine.getBgClass());
       
   308     var listClasses = this.search_engine.getBgClass().split(" ");
       
   309     var classes = "";
       
   310     for	(var i= 0; i < listClasses.length; i++) {
       
   311         classes += "." + listClasses[i];
       
   312     }
       
   313     this.$.find(".Rk-Web-Search-Input.Rk-Search-Input").attr("placeholder", this.translate("Search in ") + this.$.find(".Rk-Search-List "+ classes).html());
       
   314 };
       
   315 
       
   316 Renkan.prototype.resizeBins = function() {
       
   317     var _d = + this.$.find(".Rk-Bins-Head").outerHeight();
       
   318     this.$.find(".Rk-Bin-Title:visible").each(function() {
       
   319         _d += Rkns.$(this).outerHeight();
       
   320     });
       
   321     this.$.find(".Rk-Bin-Main").css({
       
   322         height: this.$.find(".Rk-Bins").height() - _d
       
   323     });
       
   324 };
       
   325 
       
   326 /* Utility functions */
       
   327 var getUUID4 = function() {
       
   328     return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
       
   329         var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
       
   330         return v.toString(16);
       
   331     });
       
   332 };
       
   333 
       
   334 Rkns.Utils = {
       
   335     getUUID4 : getUUID4,
       
   336     getUID : (function() {
       
   337         function pad(n){
       
   338             return n<10 ? '0'+n : n;
       
   339         }
       
   340         var _d = new Date(),
       
   341             ID_AUTO_INCREMENT = 0,
       
   342             ID_BASE = _d.getUTCFullYear() + '-' +
       
   343               pad(_d.getUTCMonth()+1) + '-' +
       
   344               pad(_d.getUTCDate()) + '-' +
       
   345               getUUID4();
       
   346         return function(_base) {
       
   347             var _n = (++ID_AUTO_INCREMENT).toString(16),
       
   348                 _uidbase = (typeof _base === "undefined" ? "" : _base + "-" );
       
   349             while (_n.length < 4) { _n = '0' + _n; }
       
   350             return _uidbase + ID_BASE + '-' + _n;
       
   351         };
       
   352     })(),
       
   353     getFullURL : function(url) {
       
   354 
       
   355         if(typeof(url) === 'undefined' || url == null ) {
       
   356             return "";
       
   357         }
       
   358         if(/https?:\/\//.test(url)) {
       
   359             return url;
       
   360         }
       
   361         var img = new Image();
       
   362         img.src = url;
       
   363         var res = img.src;
       
   364         img.src = null;
       
   365         return res;
       
   366 
       
   367     },
       
   368     inherit : function(_baseClass, _callbefore) {
       
   369 
       
   370         var _class = function(_arg) {
       
   371             if (typeof _callbefore === "function") {
       
   372                 _callbefore.apply(this, Array.prototype.slice.call(arguments, 0));
       
   373             }
       
   374             _baseClass.apply(this, Array.prototype.slice.call(arguments, 0));
       
   375             if (typeof this._init === "function" && !this._initialized) {
       
   376                 this._init.apply(this, Array.prototype.slice.call(arguments, 0));
       
   377                 this._initialized = true;
       
   378             }
       
   379         };
       
   380         _.extend(_class.prototype,_baseClass.prototype);
       
   381 
       
   382         return _class;
       
   383 
       
   384     },
       
   385     regexpFromTextOrArray: (function() {
       
   386         var charsub = [
       
   387                 '[aáàâä]',
       
   388                 '[cç]',
       
   389                 '[eéèêë]',
       
   390                 '[iíìîï]',
       
   391                 '[oóòôö]',
       
   392                 '[uùûü]'
       
   393             ],
       
   394             removeChars = [
       
   395                 String.fromCharCode(768), String.fromCharCode(769), String.fromCharCode(770), String.fromCharCode(771), String.fromCharCode(807),
       
   396                 "{", "}", "(", ")", "[", "]", "【", "】", "、", "・", "‥", "。", "「", "」", "『", "』", "〜", ":", "!", "?", " ",
       
   397                 ",", " ", ";", "(", ")", ".", "*", "+", "\\", "?", "|", "{", "}", "[", "]", "^", "#", "/"
       
   398             ],
       
   399             remsrc = "[\\" + removeChars.join("\\") + "]",
       
   400             remrx = new RegExp(remsrc, "gm"),
       
   401             charsrx = _.map(charsub, function(c) {
       
   402                 return new RegExp(c);
       
   403             });
       
   404 
       
   405         function replaceText(_text) {
       
   406             var txt = _text.toLowerCase().replace(remrx,""), src = "";
       
   407             function makeReplaceFunc(l) {
       
   408               return function(k,v) {
       
   409                 l = l.replace(charsrx[k], v);
       
   410               };
       
   411             }
       
   412             for (var j = 0; j < txt.length; j++) {
       
   413                 if (j) {
       
   414                     src += remsrc + "*";
       
   415                 }
       
   416                 var l = txt[j];
       
   417                 _.each(charsub, makeReplaceFunc(l));
       
   418                 src += l;
       
   419             }
       
   420             return src;
       
   421         }
       
   422 
       
   423         function getSource(inp) {
       
   424             switch (typeof inp) {
       
   425                 case "string":
       
   426                     return replaceText(inp);
       
   427                 case "object":
       
   428                     var src = '';
       
   429                     _.each(inp, function(v) {
       
   430                         var res = getSource(v);
       
   431                         if (res) {
       
   432                             if (src) {
       
   433                                 src += '|';
       
   434                             }
       
   435                             src += res;
       
   436                         }
   484                         }
   437                     });
   485                     };
   438                     return src;
   486                 }
   439             }
   487             };
   440             return '';
   488         })(),
   441         }
   489         /* The minimum distance (in pixels) the mouse has to move to consider an element was dragged */
   442 
   490         _MIN_DRAG_DISTANCE: 2,
   443         return function(_textOrArray) {
   491         /* Distance between the inner and outer radius of buttons that appear when hovering on a node */
   444             var source = getSource(_textOrArray);
   492         _NODE_BUTTON_WIDTH: 40,
   445             if (source) {
   493 
   446                 var testrx = new RegExp( source, "im"),
   494         _EDGE_BUTTON_INNER: 2,
   447                     replacerx = new RegExp( '(' + source + ')', "igm");
   495         _EDGE_BUTTON_OUTER: 40,
   448                 return {
   496         /* Constants used to know if a specific action is to be performed when clicking on the canvas */
   449                     isempty: false,
   497         _CLICKMODE_ADDNODE: 1,
   450                     source: source,
   498         _CLICKMODE_STARTEDGE: 2,
   451                     test: function(_t) { return testrx.test(_t); },
   499         _CLICKMODE_ENDEDGE: 3,
   452                     replace: function(_text, _replace) { return _text.replace(replacerx, _replace); }
   500         /* Node size step: Used to calculate the size change when clicking the +/- buttons */
   453                 };
   501         _NODE_SIZE_STEP: Math.LN2 / 4,
   454             } else {
   502         _MIN_SCALE: 1 / 20,
   455                 return {
   503         _MAX_SCALE: 20,
   456                     isempty: true,
   504         _MOUSEMOVE_RATE: 80,
   457                     source: '',
   505         _DOUBLETAP_DELAY: 800,
   458                     test: function() { return true; },
   506         /* Maximum distance in pixels (squared, to reduce calculations)
   459                     replace: function(_text) { return text; }
   507          * between two taps when double-tapping on a touch terminal */
   460                 };
   508         _DOUBLETAP_DISTANCE: 20 * 20,
   461             }
   509         /* A placeholder so a default colour is displayed when a node has a null value for its user property */
   462         };
   510         _USER_PLACEHOLDER: function(_renkan) {
   463     })(),
   511             return {
   464     /* The minimum distance (in pixels) the mouse has to move to consider an element was dragged */
   512                 color: _renkan.options.default_user_color,
   465     _MIN_DRAG_DISTANCE: 2,
   513                 title: _renkan.translate("(unknown user)"),
   466     /* Distance between the inner and outer radius of buttons that appear when hovering on a node */
   514                 get: function(attr) {
   467     _NODE_BUTTON_WIDTH: 40,
   515                     return this[attr] || false;
   468 
   516                 }
   469     _EDGE_BUTTON_INNER: 2,
   517             };
   470     _EDGE_BUTTON_OUTER: 40,
   518         },
   471     /* Constants used to know if a specific action is to be performed when clicking on the canvas */
   519         /* The code for the "Drag and Add Bookmarklet", slightly minified and with whitespaces removed, though
   472     _CLICKMODE_ADDNODE: 1,
   520          * it doesn't seem that it's still a requirement in newer browsers (i.e. the ones compatibles with canvas drawing)
   473     _CLICKMODE_STARTEDGE: 2,
   521          */
   474     _CLICKMODE_ENDEDGE: 3,
   522         _BOOKMARKLET_CODE: function(_renkan) {
   475     /* Node size step: Used to calculate the size change when clicking the +/- buttons */
   523             return "(function(a,b,c,d,e,f,h,i,j,k,l,m,n,o,p,q,r){a=document;b=a.body;c=a.location.href;j='draggable';m='text/x-iri-';d=a.createElement('div');d.innerHTML='<p_style=\"position:fixed;top:0;right:0;font:bold_18px_sans-serif;color:#fff;background:#909;padding:10px;z-index:100000;\">" +
   476     _NODE_SIZE_STEP: Math.LN2/4,
   524                 _renkan.translate("Drag items from this website, drop them in Renkan").replace(/ /g, "_") +
   477     _MIN_SCALE: 1/20,
   525                 "</p>'.replace(/_/g,String.fromCharCode(32));b.appendChild(d);e=[{r:/https?:\\/\\/[^\\/]*twitter\\.com\\//,s:'.tweet',n:'twitter'},{r:/https?:\\/\\/[^\\/]*google\\.[^\\/]+\\//,s:'.g',n:'google'},{r:/https?:\\/\\/[^\\/]*lemonde\\.fr\\//,s:'[data-vr-contentbox]',n:'lemonde'}];f=false;e.forEach(function(g){if(g.r.test(c)){f=g;}});if(f){h=function(){Array.prototype.forEach.call(a.querySelectorAll(f.s),function(i){i[j]=true;k=i.style;k.borderWidth='2px';k.borderColor='#909';k.borderStyle='solid';k.backgroundColor='rgba(200,0,180,.1)';})};window.setInterval(h,500);h();};a.addEventListener('dragstart',function(k){l=k.dataTransfer;l.setData(m+'source-uri',c);l.setData(m+'source-title',a.title);n=k.target;if(f){o=n;while(!o.attributes[j]){o=o.parentNode;if(o==b){break;}}}if(f&&o.attributes[j]){p=o.cloneNode(true);l.setData(m+'specific-site',f.n)}else{q=a.getSelection();if(q.type==='Range'||!q.type){p=q.getRangeAt(0).cloneContents();}else{p=n.cloneNode();}}r=a.createElement('div');r.appendChild(p);l.setData('text/x-iri-selected-text',r.textContent.trim());l.setData('text/x-iri-selected-html',r.innerHTML);},false);})();";
   478     _MAX_SCALE: 20,
   526         },
   479     _MOUSEMOVE_RATE: 80,
   527         /* Shortens text to the required length then adds ellipsis */
   480     _DOUBLETAP_DELAY: 800,
   528         shortenText: function(_text, _maxlength) {
   481     /* Maximum distance in pixels (squared, to reduce calculations)
   529             return (_text.length > _maxlength ? (_text.substr(0, _maxlength) + '…') : _text);
   482      * between two taps when double-tapping on a touch terminal */
   530         },
   483     _DOUBLETAP_DISTANCE: 20*20,
   531         /* Drawing an edit box with an arrow and positioning the edit box according to the position of the node/edge being edited
   484     /* A placeholder so a default colour is displayed when a node has a null value for its user property */
   532          * Called by Rkns.Renderer.NodeEditor and Rkns.Renderer.EdgeEditor */
   485     _USER_PLACEHOLDER: function(_renkan) {
   533         drawEditBox: function(_options, _coords, _path, _xmargin, _selector) {
   486         return {
   534             _selector.css({
   487             color: _renkan.options.default_user_color,
   535                 width: (_options.tooltip_width - 2 * _options.tooltip_padding)
   488             title: _renkan.translate("(unknown user)"),
   536             });
   489             get: function(attr) {
   537             var _height = _selector.outerHeight() + 2 * _options.tooltip_padding,
   490                 return this[attr] || false;
   538                 _isLeft = (_coords.x < paper.view.center.x ? 1 : -1),
   491             }
   539                 _left = _coords.x + _isLeft * (_xmargin + _options.tooltip_arrow_length),
   492         };
   540                 _right = _coords.x + _isLeft * (_xmargin + _options.tooltip_arrow_length + _options.tooltip_width),
   493     },
   541                 _top = _coords.y - _height / 2;
   494     /* The code for the "Drag and Add Bookmarklet", slightly minified and with whitespaces removed, though
   542             if (_top + _height > (paper.view.size.height - _options.tooltip_margin)) {
   495      * it doesn't seem that it's still a requirement in newer browsers (i.e. the ones compatibles with canvas drawing)
   543                 _top = Math.max(paper.view.size.height - _options.tooltip_margin, _coords.y + _options.tooltip_arrow_width / 2) - _height;
   496      */
   544             }
   497     _BOOKMARKLET_CODE: function(_renkan) {
   545             if (_top < _options.tooltip_margin) {
   498         return "(function(a,b,c,d,e,f,h,i,j,k,l,m,n,o,p,q,r){a=document;b=a.body;c=a.location.href;j='draggable';m='text/x-iri-';d=a.createElement('div');d.innerHTML='<p_style=\"position:fixed;top:0;right:0;font:bold_18px_sans-serif;color:#fff;background:#909;padding:10px;z-index:100000;\">" +
   546                 _top = Math.min(_options.tooltip_margin, _coords.y - _options.tooltip_arrow_width / 2);
   499         _renkan.translate("Drag items from this website, drop them in Renkan").replace(/ /g,"_") +
   547             }
   500         "</p>'.replace(/_/g,String.fromCharCode(32));b.appendChild(d);e=[{r:/https?:\\/\\/[^\\/]*twitter\\.com\\//,s:'.tweet',n:'twitter'},{r:/https?:\\/\\/[^\\/]*google\\.[^\\/]+\\//,s:'.g',n:'google'},{r:/https?:\\/\\/[^\\/]*lemonde\\.fr\\//,s:'[data-vr-contentbox]',n:'lemonde'}];f=false;e.forEach(function(g){if(g.r.test(c)){f=g;}});if(f){h=function(){Array.prototype.forEach.call(a.querySelectorAll(f.s),function(i){i[j]=true;k=i.style;k.borderWidth='2px';k.borderColor='#909';k.borderStyle='solid';k.backgroundColor='rgba(200,0,180,.1)';})};window.setInterval(h,500);h();};a.addEventListener('dragstart',function(k){l=k.dataTransfer;l.setData(m+'source-uri',c);l.setData(m+'source-title',a.title);n=k.target;if(f){o=n;while(!o.attributes[j]){o=o.parentNode;if(o==b){break;}}}if(f&&o.attributes[j]){p=o.cloneNode(true);l.setData(m+'specific-site',f.n)}else{q=a.getSelection();if(q.type==='Range'||!q.type){p=q.getRangeAt(0).cloneContents();}else{p=n.cloneNode();}}r=a.createElement('div');r.appendChild(p);l.setData('text/x-iri-selected-text',r.textContent.trim());l.setData('text/x-iri-selected-html',r.innerHTML);},false);})();";
   548             var _bottom = _top + _height;
   501     },
   549             /* jshint laxbreak:true */
   502     /* Shortens text to the required length then adds ellipsis */
   550             _path.segments[0].point = _path.segments[7].point = _coords.add([_isLeft * _xmargin, 0]);
   503     shortenText: function(_text, _maxlength) {
   551             _path.segments[1].point.x = _path.segments[2].point.x = _path.segments[5].point.x = _path.segments[6].point.x = _left;
   504         return (_text.length > _maxlength ? (_text.substr(0,_maxlength) + '…') : _text);
   552             _path.segments[3].point.x = _path.segments[4].point.x = _right;
   505     },
   553             _path.segments[2].point.y = _path.segments[3].point.y = _top;
   506     /* Drawing an edit box with an arrow and positioning the edit box according to the position of the node/edge being edited
   554             _path.segments[4].point.y = _path.segments[5].point.y = _bottom;
   507      * Called by Rkns.Renderer.NodeEditor and Rkns.Renderer.EdgeEditor */
   555             _path.segments[1].point.y = _coords.y - _options.tooltip_arrow_width / 2;
   508     drawEditBox: function(_options, _coords, _path, _xmargin, _selector) {
   556             _path.segments[6].point.y = _coords.y + _options.tooltip_arrow_width / 2;
   509         _selector.css({
   557             _path.closed = true;
   510             width: ( _options.tooltip_width - 2* _options.tooltip_padding )
   558             _path.fillColor = new paper.GradientColor(new paper.Gradient([_options.tooltip_top_color, _options.tooltip_bottom_color]), [0, _top], [0, _bottom]);
   511         });
   559             _selector.css({
   512         var _height = _selector.outerHeight() + 2* _options.tooltip_padding,
   560                 left: (_options.tooltip_padding + Math.min(_left, _right)),
   513         _isLeft = (_coords.x < paper.view.center.x ? 1 : -1),
   561                 top: (_options.tooltip_padding + _top)
   514         _left = _coords.x + _isLeft * ( _xmargin + _options.tooltip_arrow_length ),
   562             });
   515         _right = _coords.x + _isLeft * ( _xmargin + _options.tooltip_arrow_length + _options.tooltip_width ),
   563             return _path;
   516         _top = _coords.y - _height / 2;
   564         }
   517         if (_top + _height > (paper.view.size.height - _options.tooltip_margin)) {
   565     };
   518             _top = Math.max( paper.view.size.height - _options.tooltip_margin, _coords.y + _options.tooltip_arrow_width / 2 ) - _height;
       
   519         }
       
   520         if (_top < _options.tooltip_margin) {
       
   521             _top = Math.min( _options.tooltip_margin, _coords.y - _options.tooltip_arrow_width / 2 );
       
   522         }
       
   523         var _bottom = _top + _height;
       
   524         /* jshint laxbreak:true */
       
   525         _path.segments[0].point
       
   526           = _path.segments[7].point
       
   527           = _coords.add([_isLeft * _xmargin, 0]);
       
   528         _path.segments[1].point.x
       
   529           = _path.segments[2].point.x
       
   530           = _path.segments[5].point.x
       
   531           = _path.segments[6].point.x
       
   532           = _left;
       
   533         _path.segments[3].point.x
       
   534           = _path.segments[4].point.x
       
   535           = _right;
       
   536         _path.segments[2].point.y
       
   537           = _path.segments[3].point.y
       
   538           = _top;
       
   539         _path.segments[4].point.y
       
   540           = _path.segments[5].point.y
       
   541           = _bottom;
       
   542         _path.segments[1].point.y = _coords.y - _options.tooltip_arrow_width / 2;
       
   543         _path.segments[6].point.y = _coords.y + _options.tooltip_arrow_width / 2;
       
   544         _path.closed = true;
       
   545         _path.fillColor = new paper.GradientColor(new paper.Gradient([_options.tooltip_top_color, _options.tooltip_bottom_color]), [0,_top], [0, _bottom]);
       
   546         _selector.css({
       
   547             left: (_options.tooltip_padding + Math.min(_left, _right)),
       
   548             top: (_options.tooltip_padding + _top)
       
   549         });
       
   550         return _path;
       
   551     }
       
   552 };
       
   553 })(window);
   566 })(window);
   554 
   567 
   555 /* END main.js */
   568 /* END main.js */