source: trunk/plugins/Linker/linker.js @ 997

Last change on this file since 997 was 997, checked in by ray, 11 years ago

#1197 [Linker] Patch for files[i].length, IE shows bug in line 262

  • Property svn:eol-style set to native
  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
File size: 15.4 KB
Line 
1/** htmlArea - James' Fork - Linker Plugin **/
2Linker._pluginInfo =
3{
4  name     : "Linker",
5  version  : "1.0",
6  developer: "James Sleeman",
7  developer_url: "http://www.gogo.co.nz/",
8  c_owner      : "Gogo Internet Services",
9  license      : "htmlArea",
10  sponsor      : "Gogo Internet Services",
11  sponsor_url  : "http://www.gogo.co.nz/"
12};
13
14Xinha.loadStyle('dTree/dtree.css', 'Linker');
15
16Xinha.Config.prototype.Linker =
17{
18  'treeCaption' : document.location.host,
19  'backend' : _editor_url + 'plugins/Linker/scan.php',
20  'backend_data' : null,
21  'files' : null
22};
23
24
25function Linker(editor, args)
26{
27  this.editor  = editor;
28  this.lConfig = editor.config.Linker;
29
30  var linker = this;
31  if(editor.config.btnList.createlink)
32  {
33    editor.config.btnList.createlink[3]
34      =  function(e, objname, obj) { linker._createLink(linker._getSelectedAnchor()); };
35  }
36  else
37  {
38    editor.config.registerButton(
39                                 'createlink', 'Insert/Modify Hyperlink', [_editor_url + "images/ed_buttons_main.gif",6,1], false,
40                                 function(e, objname, obj) { linker._createLink(linker._getSelectedAnchor()); }
41                                 );
42  }
43
44  // See if we can find 'createlink'
45 editor.config.addToolbarElement("createlink", "createlink", 0);
46}
47
48Linker.prototype._lc = function(string)
49{
50  return Xinha._lc(string, 'Linker');
51};
52
53Linker.prototype._createLink = function(a)
54{
55  if(!a && this.editor.selectionEmpty(this.editor.getSelection()))
56  {       
57    alert(this._lc("You must select some text before making a new link."));
58    return false;
59  }
60
61  var inputs =
62  {
63    type:     'url',
64    href:     'http://www.example.com/',
65    target:   '',
66    p_width:  '',
67    p_height: '',
68    p_options: ['menubar=no','toolbar=yes','location=no','status=no','scrollbars=yes','resizeable=yes'],
69    to:       'alice@example.com',
70    subject:  '',
71    body:     '',
72    anchor:   ''
73  };
74
75  if(a && a.tagName.toLowerCase() == 'a')
76  {
77    var href =this.editor.fixRelativeLinks(a.getAttribute('href'));
78    var m = href.match(/^mailto:(.*@[^?&]*)(\?(.*))?$/);
79    var anchor = href.match(/^#(.*)$/);
80
81    if(m)
82    {
83      // Mailto
84      inputs.type = 'mailto';
85      inputs.to = m[1];
86      if(m[3])
87      {
88        var args  = m[3].split('&');
89        for(var x = 0; x<args.length; x++)
90        {
91          var j = args[x].match(/(subject|body)=(.*)/);
92          if(j)
93          {
94            inputs[j[1]] = decodeURIComponent(j[2]);
95          }
96        }
97      }
98    }
99    else if (anchor)
100    {
101      //Anchor-Link
102      inputs.type = 'anchor';
103      inputs.anchor = anchor[1];
104     
105    }
106    else
107    {
108
109
110      if(a.getAttribute('onclick'))
111      {
112        var m = a.getAttribute('onclick').match(/window\.open\(\s*this\.href\s*,\s*'([a-z0-9_]*)'\s*,\s*'([a-z0-9_=,]*)'\s*\)/i);
113
114        // Popup Window
115        inputs.href   = href ? href : '';
116        inputs.target = 'popup';
117        inputs.p_name = m[1];
118        inputs.p_options = [ ];
119
120
121        var args = m[2].split(',');
122        for(var x = 0; x < args.length; x++)
123        {
124          var i = args[x].match(/(width|height)=([0-9]+)/);
125          if(i)
126          {
127            inputs['p_' + i[1]] = parseInt(i[2]);
128          }
129          else
130          {
131            inputs.p_options.push(args[x]);
132          }
133        }
134      }
135      else
136      {
137        // Normal
138        inputs.href   = href;
139        inputs.target = a.target;
140      }
141    }
142  }
143
144  var linker = this;
145
146  // If we are not editing a link, then we need to insert links now using execCommand
147  // because for some reason IE is losing the selection between now and when doOK is
148  // complete.  I guess because we are defocusing the iframe when we click stuff in the
149  // linker dialog.
150
151  this.a = a; // Why doesn't a get into the closure below, but if I set it as a property then it's fine?
152
153  var doOK = function()
154  {
155    //if(linker.a) alert(linker.a.tagName);
156    var a = linker.a;
157
158    var values = linker._dialog.hide();
159    var atr =
160    {
161      href: '',
162      target:'',
163      title:'',
164      onclick:''
165    };
166
167    if(values.type == 'url')
168    {
169     if(values.href)
170     {
171       atr.href = values.href;
172       atr.target = values.target;
173       if(values.target == 'popup')
174       {
175
176         if(values.p_width)
177         {
178           values.p_options.push('width=' + values.p_width);
179         }
180         if(values.p_height)
181         {
182           values.p_options.push('height=' + values.p_height);
183         }
184         atr.onclick = 'if(window.top && window.top.Xinha){return false}window.open(this.href, \'' + (values.p_name.replace(/[^a-z0-9_]/i, '_')) + '\', \'' + values.p_options.join(',') + '\');return false;';
185       }
186     }
187    }
188    else if(values.type == 'anchor')
189    {
190      if(values.anchor)
191      {
192        atr.href = values.anchor.value;
193      }
194    }
195    else
196    {
197      if(values.to)
198      {
199        atr.href = 'mailto:' + values.to;
200        if(values.subject) atr.href += '?subject=' + encodeURIComponent(values.subject);
201        if(values.body)    atr.href += (values.subject ? '&' : '?') + 'body=' + encodeURIComponent(values.body);
202      }
203    }
204
205    if(a && a.tagName.toLowerCase() == 'a')
206    {
207      if(!atr.href)
208      {
209        if(confirm(linker._dialog._lc('Are you sure you wish to remove this link?')))
210        {
211          var p = a.parentNode;
212          while(a.hasChildNodes())
213          {
214            p.insertBefore(a.removeChild(a.childNodes[0]), a);
215          }
216          p.removeChild(a);
217          linker.editor.updateToolbar();
218          return;
219        }
220      }
221      else
222      {
223        // Update the link
224        for(var i in atr)
225        {
226          a.setAttribute(i, atr[i]);
227        }
228       
229        // If we change a mailto link in IE for some hitherto unknown
230        // reason it sets the innerHTML of the link to be the
231        // href of the link.  Stupid IE.
232        if(Xinha.is_ie)
233        {
234          if(/mailto:([^?<>]*)(\?[^<]*)?$/i.test(a.innerHTML))
235          {
236            a.innerHTML = RegExp.$1;
237          }
238        }
239      }
240    }
241    else
242    {
243      if(!atr.href) return true;
244
245      // Insert a link, we let the browser do this, we figure it knows best
246      var tmp = Xinha.uniq('http://www.example.com/Link');
247      linker.editor._doc.execCommand('createlink', false, tmp);
248
249      // Fix them up
250      var anchors = linker.editor._doc.getElementsByTagName('a');
251      for(var i = 0; i < anchors.length; i++)
252      {
253        var anchor = anchors[i];
254        if(anchor.href == tmp)
255        {
256          // Found one.
257          if (!a) a = anchor;
258          for(var j in atr)
259          {
260            anchor.setAttribute(j, atr[j]);
261          }
262        }
263      }
264    }
265    linker.editor.selectNodeContents(a);
266    linker.editor.updateToolbar();
267  };
268
269  this._dialog.show(inputs, doOK);
270
271};
272
273Linker.prototype._getSelectedAnchor = function()
274{
275  var sel  = this.editor.getSelection();
276  var rng  = this.editor.createRange(sel);
277  var a    = this.editor.activeElement(sel);
278  if(a != null && a.tagName.toLowerCase() == 'a')
279  {
280    return a;
281  }
282  else
283  {
284    a = this.editor._getFirstAncestor(sel, 'a');
285    if(a != null)
286    {
287      return a;
288    }
289  }
290  return null;
291};
292
293Linker.prototype.onGenerateOnce = function()
294{
295  this._dialog = new Linker.Dialog(this);
296};
297// Inline Dialog for Linker
298
299Linker.Dialog_dTrees = [ ];
300
301
302Linker.Dialog = function (linker)
303{
304  var  lDialog = this;
305  this.Dialog_nxtid = 0;
306  this.linker = linker;
307  this.id = { }; // This will be filled below with a replace, nifty
308
309  this.ready = false;
310  this.files  = false;
311  this.html   = false;
312  this.dialog = false;
313
314  // load the dTree script
315  this._prepareDialog();
316
317};
318
319Linker.Dialog.prototype._prepareDialog = function()
320{
321  var lDialog = this;
322  var linker = this.linker;
323
324  // We load some stuff up int he background, recalling this function
325  // when they have loaded.  This is to keep the editor responsive while
326  // we prepare the dialog.
327  if(typeof dTree == 'undefined')
328  {
329    Xinha._loadback(_editor_url + 'plugins/Linker/dTree/dtree.js',
330                       function() {lDialog._prepareDialog(); }
331                      );
332    return;
333  }
334
335  if(this.files === false)
336  {
337    if(linker.lConfig.backend)
338    {
339      //get files from backend
340      Xinha._postback(linker.lConfig.backend,
341                      linker.lConfig.backend_data,
342                          function(txt) {
343                            try {
344                                lDialog.files = eval(txt);
345                            } catch(Error) {
346                                lDialog.files = [ {url:'',title:Error.toString()} ];
347                            }
348                            lDialog._prepareDialog(); });
349    }
350    else if(linker.lConfig.files != null)
351    {
352        //get files from plugin-config
353        lDialog.files = linker.lConfig.files;
354        lDialog._prepareDialog();
355    }
356    return;
357  }
358  var files = this.files;
359
360  if(this.html == false)
361  {
362    Xinha._getback(_editor_url + 'plugins/Linker/dialog.html', function(txt) { lDialog.html = txt; lDialog._prepareDialog(); });
363    return;
364  }
365  var html = this.html;
366
367  // Now we have everything we need, so we can build the dialog.
368  var dialog = this.dialog = new Xinha.Dialog(linker.editor, this.html, 'Linker');
369  var dTreeName = Xinha.uniq('dTree_');
370
371  this.dTree = new dTree(dTreeName, _editor_url + 'plugins/Linker/dTree/');
372  eval(dTreeName + ' = this.dTree');
373
374  this.dTree.add(this.Dialog_nxtid++, -1, linker.lConfig.treeCaption , null, linker.lConfig.treeCaption);
375  this.makeNodes(files, 0);
376
377  // Put it in
378  var ddTree = this.dialog.getElementById('dTree');
379  //ddTree.innerHTML = this.dTree.toString();
380  ddTree.innerHTML = '';
381  ddTree.style.position = 'absolute';
382  ddTree.style.left = 1 + 'px';
383  ddTree.style.top =  0 + 'px';
384  ddTree.style.overflow = 'auto';
385  ddTree.style.backgroundColor = 'white';
386  this.ddTree = ddTree;
387  this.dTree._linker_premade = this.dTree.toString();
388
389  var options = this.dialog.getElementById('options');
390  options.style.position = 'absolute';
391  options.style.top      = 0   + 'px';
392  options.style.right    = 0   + 'px';
393  options.style.width    = 320 + 'px';
394  options.style.overflow = 'auto';
395
396  // Hookup the resizer
397  this.dialog.onresize = function()
398    {
399      var h = parseInt(dialog.height) - dialog.getElementById('h1').offsetHeight;
400      var w = parseInt(dialog.width)  - 322 ;
401      // An error is thrown with IE when trying to set a negative width or a negative height
402      // But perhaps a width / height of 0 is not the minimum required we need to set
403      if (w<0) w = 0;
404      if (h<0) h = 0;
405      options.style.height = ddTree.style.height = h + 'px';
406      ddTree.style.width  = w + 'px';
407    }
408
409  this.ready = true;
410};
411
412Linker.Dialog.prototype.makeNodes = function(files, parent)
413{
414  for(var i = 0; i < files.length; i++)
415  {
416    if(typeof files[i] == 'string')
417    {
418      this.dTree.add(Linker.nxtid++, parent,
419                     files[i].replace(/^.*\//, ''),
420                     'javascript:document.getElementsByName(\'' + this.dialog.id.href + '\')[0].value=decodeURIComponent(\'' + encodeURIComponent(files[i]) + '\');document.getElementsByName(\'' + this.dialog.id.type + '\')[0].click();document.getElementsByName(\'' + this.dialog.id.href + '\')[0].focus();void(0);',
421                     files[i]);
422    }
423    else if(typeof files[i]=="object" && files[i] && typeof files[i].length==="number") // there seems to be a strange bug in IE that requires this complicated check, see #1197
424    {
425      var id = this.Dialog_nxtid++;
426      this.dTree.add(id, parent, files[i][0].replace(/^.*\//, ''), null, files[i][0]);
427      this.makeNodes(files[i][1], id);
428    }
429    else if(typeof files[i] == 'object')
430    {
431      if(files[i].children) {
432        var id = this.Dialog_nxtid++;
433      } else {
434        var id = Linker.nxtid++;
435      }
436
437      if(files[i].title) var title = files[i].title;
438      else if(files[i].url) var title = files[i].url.replace(/^.*\//, '');
439      else var title = "no title defined";
440      if(files[i].url) var link = 'javascript:document.getElementsByName(\'' + this.dialog.id.href + '\')[0].value=decodeURIComponent(\'' + encodeURIComponent(files[i].url) + '\');document.getElementsByName(\'' + this.dialog.id.type + '\')[0].click();document.getElementsByName(\'' + this.dialog.id.href + '\')[0].focus();void(0);';
441      else var link = '';
442     
443      this.dTree.add(id, parent, title, link, title);
444      if(files[i].children) {
445        this.makeNodes(files[i].children, id);
446      }
447    }
448  }
449};
450
451Linker.Dialog.prototype._lc = Linker.prototype._lc;
452
453Linker.Dialog.prototype.show = function(inputs, ok, cancel)
454{
455  if(!this.ready)
456  {
457    var lDialog = this;
458    window.setTimeout(function() {lDialog.show(inputs,ok,cancel);},100);
459    return;
460  }
461
462  if(this.ddTree.innerHTML == '')
463  {
464    this.ddTree.innerHTML = this.dTree._linker_premade;
465  }
466
467  if(inputs.type=='url')
468  {
469    this.dialog.getElementById('urltable').style.display = '';
470    this.dialog.getElementById('mailtable').style.display = 'none';
471    this.dialog.getElementById('anchortable').style.display = 'none';
472  }
473  else if(inputs.type=='anchor')
474  {
475    this.dialog.getElementById('urltable').style.display = 'none';
476    this.dialog.getElementById('mailtable').style.display = 'none';
477    this.dialog.getElementById('anchortable').style.display = '';
478  }
479  else
480  {
481    this.dialog.getElementById('urltable').style.display = 'none';
482    this.dialog.getElementById('mailtable').style.display = '';
483    this.dialog.getElementById('anchortable').style.display = 'none';
484  }
485
486  if(inputs.target=='popup')
487  {
488    this.dialog.getElementById('popuptable').style.display = '';
489  }
490  else
491  {
492    this.dialog.getElementById('popuptable').style.display = 'none';
493  }
494 
495  var anchor = this.dialog.getElementById('anchor');
496  for(var i=anchor.length;i>=0;i--) {
497    anchor[i] = null;
498  }
499
500  var html = this.linker.editor.getHTML(); 
501  var anchors = new Array();
502
503  var m = html.match(/<a[^>]+name="([^"]+)"/gi);
504  if(m)
505  {
506    for(i=0;i<m.length;i++)
507    {
508        var n = m[i].match(/name="([^"]+)"/i);
509        if(!anchors.contains(n[1])) anchors.push(n[1]);
510    }
511  }
512  m = html.match(/id="([^"]+)"/gi);
513  if(m)
514  {
515    for(i=0;i<m.length;i++)
516    {
517        n = m[i].match(/id="([^"]+)"/i);
518        if(!anchors.contains(n[1])) anchors.push(n[1]);
519    }
520  }
521 
522  for(i=0;i<anchors.length;i++)
523  {
524    var opt = new Option(anchors[i],'#'+anchors[i],false,(inputs.anchor == anchors[i]));
525    anchor[anchor.length] = opt;
526  }
527
528  //if no anchors found completely hide Anchor-Link
529  if(anchor.length==0) {
530    this.dialog.getElementById('anchorfieldset').style.display = "none";
531  }
532 
533  // if we're not editing an existing link, hide the remove link button
534  if (inputs.href == 'http://www.example.com/' && inputs.to == 'alice@example.com') {
535    this.dialog.getElementById('clear').style.display = "none";
536  }
537  else {
538    this.dialog.getElementById('clear').style.display = "";
539  }
540  // Connect the OK and Cancel buttons
541  var dialog = this.dialog;
542  var lDialog = this;
543  if(ok)
544  {
545    this.dialog.getElementById('ok').onclick = ok;
546  }
547  else
548  {
549    this.dialog.getElementById('ok').onclick = function() {lDialog.hide();};
550  }
551
552  if(cancel)
553  {
554    this.dialog.getElementById('cancel').onclick = cancel;
555  }
556  else
557  {
558    this.dialog.getElementById('cancel').onclick = function() { lDialog.hide()};
559  }
560
561  // Show the dialog
562  this.linker.editor.disableToolbar(['fullscreen','linker']);
563
564  this.dialog.show(inputs);
565
566  // Init the sizes
567  this.dialog.onresize();
568};
569
570Linker.Dialog.prototype.hide = function()
571{
572  this.linker.editor.enableToolbar();
573  return this.dialog.hide();
574};
Note: See TracBrowser for help on using the repository browser.