includes/clientside/static/functions.js
changeset 582 a38876c0793c
child 592 27377179fe58
equal deleted inserted replaced
581:5e8fd89c02ea 582:a38876c0793c
       
     1 // all utility functions go in here
       
     2 
       
     3 function makeUrl(page, query, html_friendly)
       
     4 {
       
     5   url = contentPath+page;
       
     6   if(url.indexOf('?') > 0) sep = '&';
       
     7   else sep = '?';
       
     8   if(query)
       
     9   {
       
    10     url = url + sep + query;
       
    11   }
       
    12   if(html_friendly)
       
    13   {
       
    14     url = url.replace('&', '&');
       
    15     url = url.replace('<', '&lt;');
       
    16     url = url.replace('>', '&gt;');
       
    17   }
       
    18   return url;
       
    19 }
       
    20 
       
    21 function makeUrlNS(namespace, page, query, html_friendly)
       
    22 {
       
    23   var url = contentPath+namespace_list[namespace]+(page.replace(/ /g, '_'));
       
    24   if(url.indexOf('?') > 0) sep = '&';
       
    25   else sep = '?';
       
    26   if(query)
       
    27   {
       
    28     url = url + sep + query;
       
    29   }
       
    30   if(html_friendly)
       
    31   {
       
    32     url = url.replace('&', '&amp;');
       
    33     url = url.replace('<', '&lt;');
       
    34     url = url.replace('>', '&gt;');
       
    35   }
       
    36   return append_sid(url);
       
    37 }
       
    38 
       
    39 function strToPageID(string)
       
    40 {
       
    41   // Convert Special:UploadFile to ['UploadFile', 'Special'], but convert 'Image:Enano.png' to ['Enano.png', 'File']
       
    42   for(var i in namespace_list)
       
    43     if(namespace_list[i] != '')
       
    44       if(namespace_list[i] == string.substr(0, namespace_list[i].length))
       
    45         return [string.substr(namespace_list[i].length), i];
       
    46   return [string, 'Article'];
       
    47 }
       
    48 
       
    49 function append_sid(url)
       
    50 {
       
    51   sep = ( url.indexOf('?') > 0 ) ? '&' : '?';
       
    52   if(ENANO_SID.length > 10)
       
    53   {
       
    54     url = url + sep + 'auth=' + ENANO_SID;
       
    55     sep = '&';
       
    56   }
       
    57   if ( pagepass.length > 0 )
       
    58   {
       
    59     url = url + sep + 'pagepass=' + pagepass;
       
    60   }
       
    61   return url;
       
    62 }
       
    63 
       
    64 var stdAjaxPrefix = append_sid(scriptPath+'/ajax.php?title='+title);
       
    65 
       
    66 /**
       
    67  * Core AJAX library
       
    68  */
       
    69 
       
    70 function ajaxMakeXHR()
       
    71 {
       
    72   var ajax;
       
    73   if (window.XMLHttpRequest) {
       
    74     ajax = new XMLHttpRequest();
       
    75   } else {
       
    76     if (window.ActiveXObject) {           
       
    77       ajax = new ActiveXObject("Microsoft.XMLHTTP");
       
    78     } else {
       
    79       alert('Enano client-side runtime error: No AJAX support, unable to continue');
       
    80       return;
       
    81     }
       
    82   }
       
    83   return ajax;
       
    84 }
       
    85 
       
    86 function ajaxGet(uri, f, call_editor_safe) {
       
    87   // Is the editor open?
       
    88   if ( editor_open && !call_editor_safe )
       
    89   {
       
    90     // Make sure the user is willing to close the editor
       
    91     var conf = confirm($lang.get('editor_msg_confirm_ajax'));
       
    92     if ( !conf )
       
    93     {
       
    94       // Kill off any "loading" windows, etc. and cancel the request
       
    95       unsetAjaxLoading();
       
    96       return false;
       
    97     }
       
    98     // The user allowed the editor to be closed. Reset flags and knock out the on-close confirmation.
       
    99     editor_open = false;
       
   100     enableUnload();
       
   101   }
       
   102   ajax = ajaxMakeXHR();
       
   103   if ( !ajax )
       
   104   {
       
   105     console.error('ajaxMakeXHR() failed');
       
   106     return false;
       
   107   }
       
   108   ajax.onreadystatechange = f;
       
   109   ajax.open('GET', uri, true);
       
   110   ajax.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
       
   111   ajax.send(null);
       
   112 }
       
   113 
       
   114 function ajaxPost(uri, parms, f, call_editor_safe) {
       
   115   // Is the editor open?
       
   116   if ( editor_open && !call_editor_safe )
       
   117   {
       
   118     // Make sure the user is willing to close the editor
       
   119     var conf = confirm($lang.get('editor_msg_confirm_ajax'));
       
   120     if ( !conf )
       
   121     {
       
   122       // Kill off any "loading" windows, etc. and cancel the request
       
   123       unsetAjaxLoading();
       
   124       return false;
       
   125     }
       
   126     // The user allowed the editor to be closed. Reset flags and knock out the on-close confirmation.
       
   127     editor_open = false;
       
   128     enableUnload();
       
   129   }
       
   130   ajax = ajaxMakeXHR();
       
   131   if ( !ajax )
       
   132   {
       
   133     console.error('ajaxMakeXHR() failed');
       
   134     return false;
       
   135   }
       
   136   ajax.onreadystatechange = f;
       
   137   ajax.open('POST', uri, true);
       
   138   ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
       
   139   // Setting Content-length in Safari triggers a warning
       
   140   if ( !is_Safari )
       
   141   {
       
   142     ajax.setRequestHeader("Content-length", parms.length);
       
   143   }
       
   144   ajax.setRequestHeader("Connection", "close");
       
   145   ajax.send(parms);
       
   146 }
       
   147 
       
   148 /**
       
   149  * Show a friendly error message depicting an AJAX response that is not valid JSON
       
   150  * @param string Response text
       
   151  * @param string Custom error message. If omitted, the default will be shown.
       
   152  */
       
   153 
       
   154 function handle_invalid_json(response, customerror)
       
   155 {
       
   156   var mainwin = $dynano('ajaxEditContainer').object;
       
   157   mainwin.innerHTML = '';
       
   158   
       
   159   // Title
       
   160   var h3 = document.createElement('h3');
       
   161   h3.appendChild(document.createTextNode('The site encountered an error while processing your request.'));
       
   162   mainwin.appendChild(h3);
       
   163   
       
   164   if ( typeof(customerror) == 'string' )
       
   165   {
       
   166     var el = document.createElement('p');
       
   167     el.appendChild(document.createTextNode(customerror));
       
   168     mainwin.appendChild(el);
       
   169   }
       
   170   else
       
   171   {
       
   172     customerror  = 'We unexpectedly received the following response from the server. The response should have been in the JSON ';
       
   173     customerror += 'serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers ';
       
   174     customerror += 'for this problem:';
       
   175     var el = document.createElement('p');
       
   176     el.appendChild(document.createTextNode(customerror));
       
   177     mainwin.appendChild(el);
       
   178     var ul = document.createElement('ul');
       
   179     var li1 = document.createElement('li');
       
   180     var li2 = document.createElement('li');
       
   181     var li3 = document.createElement('li');
       
   182     li1.appendChild(document.createTextNode('The server sent back a bad HTTP response code and thus sent an error page instead of running Enano. This indicates a possible problem with your server, and is not likely to be a bug with Enano.'));
       
   183     var osc_exception = ( window.location.hostname == 'demo.opensourcecms.com' ) ? ' This is KNOWN to be the case with the OpenSourceCMS.com demo version of Enano.' : '';
       
   184     li2.appendChild(document.createTextNode('The server sent back the expected JSON response, but also injected some code into the response that should not be there. Typically this consists of advertisement code. In this case, the administrator of this site will have to contact their web host to have advertisements disabled.' + osc_exception));
       
   185     li3.appendChild(document.createTextNode('It\'s possible that Enano triggered a PHP error or warning. In this case, you may be looking at a bug in Enano.'));
       
   186       
       
   187     ul.appendChild(li1);
       
   188     ul.appendChild(li2);
       
   189     ul.appendChild(li3);
       
   190     mainwin.appendChild(ul);
       
   191   }
       
   192   
       
   193   var p2 = document.createElement('p');
       
   194   p2.appendChild(document.createTextNode('The response received from the server is as follows:'));
       
   195   mainwin.appendChild(p2);
       
   196   
       
   197   var pre = document.createElement('pre');
       
   198   pre.appendChild(document.createTextNode(response));
       
   199   mainwin.appendChild(pre);
       
   200   
       
   201   var p3 = document.createElement('p');
       
   202   p3.appendChild(document.createTextNode('You may also choose to view the response as HTML. '));
       
   203   var a = document.createElement('a');
       
   204   a.appendChild(document.createTextNode('View as HTML...'));
       
   205   a._resp = response;
       
   206   a.id = 'invalidjson_link';
       
   207   a.onclick = function()
       
   208   {
       
   209     var mb = new MessageBox(MB_YESNO | MB_ICONEXCLAMATION, 'Do you really want to view this response as HTML?', 'If the response was changed during transmission to include malicious code, you may be allowing that malicious code to run by viewing the response as HTML. Only do this if you have reviewed the response text and have found no suspicious code in it.');
       
   210     mb.onclick['Yes'] = function()
       
   211     {
       
   212       var html = $dynano('invalidjson_link').object._resp;
       
   213       var win = window.open('about:blank', 'invalidjson_htmlwin', 'width=550,height=400,status=no,toolbars=no,toolbar=no,address=no,scroll=yes');
       
   214       win.document.write(html);
       
   215     }
       
   216     return false;
       
   217   }
       
   218   a.href = '#';
       
   219   p3.appendChild(a);
       
   220   mainwin.appendChild(p3);
       
   221 }
       
   222 
       
   223 function ajaxEscape(text)
       
   224 {
       
   225   /*
       
   226   text = escape(text);
       
   227   text = text.replace(/\+/g, '%2B', text);
       
   228   */
       
   229   text = window.encodeURIComponent(text);
       
   230   return text;
       
   231 }
       
   232 
       
   233 /**
       
   234  * String functions
       
   235  */
       
   236 
       
   237 // Equivalent to PHP trim() function
       
   238 function trim(text)
       
   239 {
       
   240   text = text.replace(/^([\s]+)/, '');
       
   241   text = text.replace(/([\s]+)$/, '');
       
   242   return text;
       
   243 }
       
   244 
       
   245 // Equivalent to PHP implode() function
       
   246 function implode(chr, arr)
       
   247 {
       
   248   if ( typeof ( arr.toJSONString ) == 'function' )
       
   249     delete(arr.toJSONString);
       
   250   
       
   251   var ret = '';
       
   252   var c = 0;
       
   253   for ( var i in arr )
       
   254   {
       
   255     if(i=='toJSONString')continue;
       
   256     if ( c > 0 )
       
   257       ret += chr;
       
   258     ret += arr[i];
       
   259     c++;
       
   260   }
       
   261   return ret;
       
   262 }
       
   263 
       
   264 function form_fetch_field(form, name)
       
   265 {
       
   266   var fields = form.getElementsByTagName('input');
       
   267   if ( fields.length < 1 )
       
   268     return false;
       
   269   for ( var i = 0; i < fields.length; i++ )
       
   270   {
       
   271     var field = fields[i];
       
   272     if ( field.name == name )
       
   273       return field;
       
   274   }
       
   275   return false;
       
   276 }
       
   277 
       
   278 function get_parent_form(o)
       
   279 {
       
   280   if ( !o.parentNode )
       
   281     return false;
       
   282   if ( o.tagName == 'FORM' )
       
   283     return o;
       
   284   var p = o.parentNode;
       
   285   while(true)
       
   286   {
       
   287     if ( p.tagName == 'FORM' )
       
   288       return p;
       
   289     else if ( !p )
       
   290       return false;
       
   291     else
       
   292       p = p.parentNode;
       
   293   }
       
   294 }
       
   295 
       
   296 function findParentForm(o)
       
   297 {
       
   298   return get_parent_form(o);
       
   299 }
       
   300 
       
   301 function domObjChangeOpac(opacity, id) {
       
   302     var object = id.style;
       
   303     object.opacity = (opacity / 100);
       
   304     object.MozOpacity = (opacity / 100);
       
   305     object.KhtmlOpacity = (opacity / 100);
       
   306     object.filter = "alpha(opacity=" + opacity + ")";
       
   307 }
       
   308 
       
   309 function getScrollOffset()
       
   310 {
       
   311   var position;
       
   312   if (self.pageYOffset)
       
   313   {
       
   314     position = self.pageYOffset;
       
   315   }
       
   316   else if (document.documentElement && document.documentElement.scrollTop)
       
   317   {
       
   318     position = document.documentElement.scrollTop;
       
   319   }
       
   320   else if (document.body)
       
   321   {
       
   322     position = document.body.scrollTop;
       
   323   }
       
   324   return position;
       
   325 }
       
   326 
       
   327 // Function to fade classes info-box, warning-box, error-box, etc.
       
   328 
       
   329 function fadeInfoBoxes()
       
   330 {
       
   331   var divs = new Array();
       
   332   d = document.getElementsByTagName('div');
       
   333   j = 0;
       
   334   for(var i in d)
       
   335   {
       
   336     if ( !d[i] )
       
   337       continue;
       
   338     if ( !d[i].tagName )
       
   339       continue;
       
   340     if(d[i].className=='info-box' || d[i].className=='error-box' || d[i].className=='warning-box' || d[i].className=='question-box')
       
   341     {
       
   342       divs[j] = d[i];
       
   343       j++;
       
   344     }
       
   345   }
       
   346   if(divs.length < 1) return;
       
   347   load_component('fat');
       
   348   for(i in divs)
       
   349   {
       
   350     if(!divs[i].id) divs[i].id = 'autofade_'+Math.floor(Math.random() * 100000);
       
   351     switch(divs[i].className)
       
   352     {
       
   353       case 'info-box':
       
   354       default:
       
   355         from = '#3333FF';
       
   356         break;
       
   357       case 'error-box':
       
   358         from = '#FF3333';
       
   359         break;
       
   360       case 'warning-box':
       
   361         from = '#FFFF33';
       
   362         break;
       
   363       case 'question-box':
       
   364         from = '#33FF33';
       
   365         break;
       
   366     }
       
   367     Fat.fade_element(divs[i].id,30,2000,from,Fat.get_bgcolor(divs[i].id));
       
   368   }
       
   369 }
       
   370 
       
   371 addOnloadHook(fadeInfoBoxes);
       
   372 
       
   373 // Alpha fades
       
   374 
       
   375 function opacity(id, opacStart, opacEnd, millisec)
       
   376 {
       
   377     var object = document.getElementById(id);
       
   378     domOpacity(object, opacStart, opacEnd, millisec);
       
   379 }
       
   380 
       
   381 var opacityDOMCache = new Object();
       
   382 function domOpacity(obj, opacStart, opacEnd, millisec) {
       
   383     //speed for each frame
       
   384     var speed = Math.round(millisec / 100);
       
   385     var timer = 0;
       
   386     
       
   387     // unique ID for this animation
       
   388     var uniqid = Math.floor(Math.random() * 1000000);
       
   389     opacityDOMCache[uniqid] = obj;
       
   390 
       
   391     //determine the direction for the blending, if start and end are the same nothing happens
       
   392     if(opacStart > opacEnd) {
       
   393         for(i = opacStart; i >= opacEnd; i--) {
       
   394             setTimeout("var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj)",(timer * speed));
       
   395             timer++;
       
   396         }
       
   397     } else if(opacStart < opacEnd) {
       
   398         for(i = opacStart; i <= opacEnd; i++)
       
   399             {
       
   400             setTimeout("var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj)",(timer * speed));
       
   401             timer++;
       
   402         }
       
   403     }
       
   404     setTimeout("delete(opacityDOMCache["+uniqid+"]);",(timer * speed));
       
   405 }
       
   406 
       
   407 // change the opacity for different browsers
       
   408 function changeOpac(opacity, id)
       
   409 {
       
   410   var object = document.getElementById(id);
       
   411   return domObjChangeOpac(opacity, object);
       
   412 }
       
   413 
       
   414 // draw a white ajax-ey "loading" box over an object
       
   415 function whiteOutElement(el)
       
   416 {
       
   417   var top = $(el).Top();
       
   418   var left = $(el).Left();
       
   419   var width = $(el).Width();
       
   420   var height = $(el).Height();
       
   421   
       
   422   var blackout = document.createElement('div');
       
   423   blackout.style.position = 'absolute';
       
   424   blackout.style.top = top + 'px';
       
   425   blackout.style.left = left + 'px';
       
   426   blackout.style.width = width + 'px';
       
   427   blackout.style.height = height + 'px';
       
   428   
       
   429   blackout.style.backgroundColor = '#FFFFFF';
       
   430   domObjChangeOpac(60, blackout);
       
   431   blackout.style.backgroundImage = 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
       
   432   blackout.style.backgroundPosition = 'center center';
       
   433   blackout.style.backgroundRepeat = 'no-repeat';
       
   434   blackout.style.zIndex = getHighestZ() + 2;
       
   435   
       
   436   var body = document.getElementsByTagName('body')[0];
       
   437   body.appendChild(blackout);
       
   438   
       
   439   return blackout;
       
   440 }
       
   441 
       
   442 // other DHTML functions
       
   443 
       
   444 function fetch_offset(obj)
       
   445 {
       
   446   var left_offset = obj.offsetLeft;
       
   447   var top_offset = obj.offsetTop;
       
   448   while ((obj = obj.offsetParent) != null) {
       
   449     left_offset += obj.offsetLeft;
       
   450     top_offset += obj.offsetTop;
       
   451   }
       
   452   return { 'left' : left_offset, 'top' : top_offset };
       
   453 }
       
   454 
       
   455 function fetch_dimensions(o) {
       
   456   var w = o.offsetWidth;
       
   457   var h = o.offsetHeight;
       
   458   return { 'w' : w, 'h' : h };
       
   459 }
       
   460 
       
   461 function findParentForm(o)
       
   462 {
       
   463   if ( o.tagName == 'FORM' )
       
   464     return o;
       
   465   while(true)
       
   466   {
       
   467     o = o.parentNode;
       
   468     if ( !o )
       
   469       return false;
       
   470     if ( o.tagName == 'FORM' )
       
   471       return o;
       
   472   }
       
   473   return false;
       
   474 }
       
   475 
       
   476 function bannerOn(text)
       
   477 {
       
   478   darken(true);
       
   479   var thediv = document.createElement('div');
       
   480   thediv.className = 'mdg-comment';
       
   481   thediv.style.padding = '0';
       
   482   thediv.style.marginLeft = '0';
       
   483   thediv.style.position = 'absolute';
       
   484   thediv.style.display = 'none';
       
   485   thediv.style.padding = '4px';
       
   486   thediv.style.fontSize = '14pt';
       
   487   thediv.id = 'mdgDynamic_bannerDiv_'+Math.floor(Math.random() * 1000000);
       
   488   thediv.innerHTML = text;
       
   489   
       
   490   var body = document.getElementsByTagName('body');
       
   491   body = body[0];
       
   492   body.appendChild(thediv);
       
   493   body.style.cursor = 'wait';
       
   494   
       
   495   thediv.style.display = 'block';
       
   496   dim = fetch_dimensions(thediv);
       
   497   thediv.style.display = 'none';
       
   498   bdim = { 'w' : getWidth(), 'h' : getHeight() };
       
   499   so = getScrollOffset();
       
   500   
       
   501   var left = (bdim['w'] / 2) - ( dim['w'] / 2 );
       
   502   
       
   503   var top  = (bdim['h'] / 2);
       
   504   top  = top - ( dim['h'] / 2 );
       
   505   
       
   506   top = top + so;
       
   507   
       
   508   thediv.style.top  = top  + 'px';
       
   509   thediv.style.left = left + 'px';
       
   510   
       
   511   thediv.style.display = 'block';
       
   512   
       
   513   return thediv.id;
       
   514 }
       
   515 
       
   516 function bannerOff(id)
       
   517 {
       
   518   e = document.getElementById(id);
       
   519   if(!e) return;
       
   520   e.innerHTML = '';
       
   521   e.style.display = 'none';
       
   522   var body = document.getElementsByTagName('body');
       
   523   body = body[0];
       
   524   body.style.cursor = 'default';
       
   525   enlighten(true);
       
   526 }
       
   527 
       
   528 function disableUnload(message)
       
   529 {
       
   530   if(typeof message != 'string') message = 'You may want to save your changes first.';
       
   531   window._unloadmsg = message;
       
   532   window.onbeforeunload = function(e)
       
   533   {
       
   534     if ( !e )
       
   535       e = window.event;
       
   536     e.returnValue = window._unloadmsg;
       
   537   }
       
   538 }
       
   539 
       
   540 function enableUnload()
       
   541 {
       
   542   window._unloadmsg = null;
       
   543   window.onbeforeunload = null;
       
   544 }
       
   545 
       
   546 /**
       
   547  * Gets the highest z-index of all divs in the document
       
   548  * @return integer
       
   549  */
       
   550 function getHighestZ()
       
   551 {
       
   552   z = 0;
       
   553   var divs = document.getElementsByTagName('div');
       
   554   for(var i = 0; i < divs.length; i++)
       
   555   {
       
   556     if(divs[i].style.zIndex > z) z = divs[i].style.zIndex;
       
   557   }
       
   558   return z;
       
   559 }
       
   560 
       
   561 function isKeyPressed(event)
       
   562 {
       
   563   if (event.shiftKey==1)
       
   564   {
       
   565     shift = true;
       
   566   }
       
   567   else
       
   568   {
       
   569     shift = false;
       
   570   }
       
   571 }
       
   572 
       
   573 function moveDiv(div, newparent)
       
   574 {
       
   575   var backup = div;
       
   576   var oldparent = div.parentNode;
       
   577   oldparent.removeChild(div);
       
   578   newparent.appendChild(backup);
       
   579 }
       
   580 
       
   581 var busyBannerID;
       
   582 function goBusy(msg)
       
   583 {
       
   584   if(!msg) msg = 'Please wait...';
       
   585   body = document.getElementsByTagName('body');
       
   586   body = body[0];
       
   587   body.style.cursor = 'wait';
       
   588   busyBannerID = bannerOn(msg);
       
   589 }
       
   590 
       
   591 function unBusy()
       
   592 {
       
   593   body = document.getElementsByTagName('body');
       
   594   body = body[0];
       
   595   body.style.cursor = 'default';
       
   596   bannerOff(busyBannerID);
       
   597 }
       
   598 
       
   599 function setAjaxLoading()
       
   600 {
       
   601   if ( document.getElementById('ajaxloadicon') )
       
   602   {
       
   603     document.getElementById('ajaxloadicon').src=ajax_load_icon;
       
   604   }
       
   605 }
       
   606 
       
   607 function unsetAjaxLoading()
       
   608 {
       
   609   if ( document.getElementById('ajaxloadicon') )
       
   610   {
       
   611     document.getElementById('ajaxloadicon').src=scriptPath + '/images/spacer.gif';
       
   612   }
       
   613 }
       
   614 
       
   615 function readCookie(name) {var nameEQ = name + "=";var ca = document.cookie.split(';');for(var i=0;i < ca.length;i++){var c = ca[i];while (c.charAt(0)==' ') c = c.substring(1,c.length);if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);}return null;}
       
   616 function createCookie(name,value,days){if (days){var date = new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires = "; expires="+date.toGMTString();}else var expires = "";document.cookie = name+"="+value+expires+"; path=/";}
       
   617 function eraseCookie(name) {createCookie(name,"",-1);}
       
   618 
       
   619 /*
       
   620  * AJAX login box (experimental)
       
   621  * Moved / rewritten in login.js
       
   622  */
       
   623 
       
   624 // Included only for API-compatibility
       
   625 function ajaxPromptAdminAuth(call_on_ok, level)
       
   626 {
       
   627   ajaxLogonInit(call_on_ok, level);
       
   628 }
       
   629 
       
   630 /**
       
   631  * Insert a DOM object _after_ the specified child.
       
   632  * @param object Parent node
       
   633  * @param object Node to insert
       
   634  * @param object Node to insert after
       
   635  */
       
   636 
       
   637 function insertAfter(parent, baby, bigsister)
       
   638 {
       
   639   try
       
   640   {
       
   641     if ( parent.childNodes[parent.childNodes.length-1] == bigsister )
       
   642       parent.appendChild(baby);
       
   643     else
       
   644       parent.insertBefore(baby, bigsister.nextSibling);
       
   645   }
       
   646   catch(e)
       
   647   {
       
   648     alert(e.toString());
       
   649     if ( window.console )
       
   650     {
       
   651       // Firebug support
       
   652       window.console.warn(e);
       
   653     }
       
   654   }
       
   655 }
       
   656 
       
   657 /**
       
   658  * Validates an e-mail address.
       
   659  * @param string E-mail address
       
   660  * @return bool
       
   661  */
       
   662 
       
   663 function validateEmail(email)
       
   664 {
       
   665   return ( email.match(/^(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)$/) ) ? true : false;
       
   666 }
       
   667 
       
   668 /**
       
   669  * Validates a username.
       
   670  * @param string Username to test
       
   671  * @return bool
       
   672  */
       
   673 
       
   674 function validateUsername(username)
       
   675 {
       
   676   var regex = new RegExp('^[^<>&\?\'"%\n\r/]+$', '');
       
   677   return ( username.match(regex) ) ? true : false;
       
   678 }
       
   679 
       
   680 /*
       
   681  * Utility functions, moved from windows.js
       
   682  */
       
   683 
       
   684 function getHeight() {
       
   685   var myHeight = 0;
       
   686   if( typeof( window.innerWidth ) == 'number' ) {
       
   687     myHeight = window.innerHeight;
       
   688   } else if( document.documentElement &&
       
   689       ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
       
   690     myHeight = document.documentElement.clientHeight;
       
   691   } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
       
   692     myHeight = document.body.clientHeight;
       
   693   }
       
   694   return myHeight;
       
   695 }
       
   696 
       
   697 function getWidth() {
       
   698   var myWidth = 0;
       
   699   if( typeof( window.innerWidth ) == 'number' ) {
       
   700     myWidth = window.innerWidth;
       
   701   } else if( document.documentElement &&
       
   702       ( document.documentElement.clientWidth || document.documentElement.clientWidth ) ) {
       
   703     myWidth = document.documentElement.clientWidth;
       
   704   } else if( document.body && ( document.body.clientWidth || document.body.clientWidth ) ) {
       
   705     myWidth = document.body.clientWidth;
       
   706   }
       
   707   return myWidth;
       
   708 }
       
   709 
       
   710 /**
       
   711  * Sanitizes a page URL string so that it can safely be stored in the database.
       
   712  * @param string Page ID to sanitize
       
   713  * @return string Cleaned text
       
   714  */
       
   715 
       
   716 function sanitize_page_id(page_id)
       
   717 {
       
   718   // Remove character escapes
       
   719   page_id = dirtify_page_id(page_id);
       
   720 
       
   721   var regex = new RegExp('[A-Za-z0-9\\[\\]\./:;\(\)@_-]', 'g');
       
   722   pid_clean = page_id.replace(regex, 'X');
       
   723   var pid_dirty = [];
       
   724   for ( var i = 0; i < pid_clean.length; i++ )
       
   725     pid_dirty[i] = pid_clean.substr(i, 1);
       
   726 
       
   727   for ( var i = 0; i < pid_dirty.length; i++ )
       
   728   {
       
   729     var chr = pid_dirty[i];
       
   730     if ( chr == 'X' )
       
   731       continue;
       
   732     var cid = chr.charCodeAt(0);
       
   733     cid = cid.toString(16).toUpperCase();
       
   734     if ( cid.length < 2 )
       
   735     {
       
   736       cid = '0' + cid;
       
   737     }
       
   738     pid_dirty[i] = "." + cid;
       
   739   }
       
   740   
       
   741   var pid_chars = [];
       
   742   for ( var i = 0; i < page_id.length; i++ )
       
   743     pid_chars[i] = page_id.substr(i, 1);
       
   744   
       
   745   var page_id_cleaned = '';
       
   746 
       
   747   for ( var id in pid_chars )
       
   748   {
       
   749     var chr = pid_chars[id];
       
   750     if ( pid_dirty[id] == 'X' )
       
   751       page_id_cleaned += chr;
       
   752     else
       
   753       page_id_cleaned += pid_dirty[id];
       
   754   }
       
   755   
       
   756   return page_id_cleaned;
       
   757 }
       
   758 
       
   759 /**
       
   760  * Removes character escapes in a page ID string
       
   761  * @param string Page ID string to dirty up
       
   762  * @return string
       
   763  */
       
   764 
       
   765 function dirtify_page_id(page_id)
       
   766 {
       
   767   // First, replace spaces with underscores
       
   768   page_id = page_id.replace(/ /g, '_');
       
   769 
       
   770   var matches = page_id.match(/\.[A-Fa-f0-9][A-Fa-f0-9]/g);
       
   771   
       
   772   if ( matches != null )
       
   773   {
       
   774     for ( var i = 0; i < matches.length; i++ )
       
   775     {
       
   776       var match = matches[i];
       
   777       var byt = (match.substr(1)).toUpperCase();
       
   778       var code = eval("0x" + byt);
       
   779       var regex = new RegExp('\\.' + byt, 'g');
       
   780       page_id = page_id.replace(regex, String.fromCharCode(code));
       
   781     }
       
   782   }
       
   783   
       
   784   return page_id;
       
   785 }
       
   786 
       
   787 /**
       
   788  * Equivalent to PHP's in_array function.
       
   789  */
       
   790 
       
   791 function in_array(needle, haystack)
       
   792 {
       
   793   for(var i in haystack)
       
   794   {
       
   795     if(haystack[i] == needle) return i;
       
   796   }
       
   797   return false;
       
   798 }