This site is developed to XHTML and CSS2 W3C standards. If you see this paragraph, your browser does not support those standards and you need to upgrade. Visit WaSP for a variety of options.

Gallery Pastebin - Paste and link to it

Posted by phinze Wed 18th Jun 2008 18:01

Description: imageareas.js
  1. /*
  2. * Gallery - a web based photo album viewer and editor
  3. * Copyright (C) 2000-2008 Bharat Mediratta
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
  18. */
  19. /**
  20. * Gallery2 ImageAreas DHTML JavaScript
  21. * Provides Javascript UI for selecting areas of images and interacts with the ImageAreas module on
  22. * the server side.
  23. *
  24. * Adapted From: Fotonotes DHTML Client (c) 2004-2005 Angus Turnbull http://www.twinhelix.com
  25. *               Developed under license to FotoNotes LLC
  26. *               Released under the Open Source License v2.1 or later.
  27. *
  28. * @package ImageAreas
  29. * @author Paul Hinze <paul.t.hinze@gmail.com>
  30. * @version $Revision$
  31. */
  32.  
  33. /**************************************
  34. * CONFIGURATION, VARIABLES AND SETUP *
  35. **************************************/
  36.  
  37. // Address of server (to be set by template)
  38. var iaServer = null;
  39. // Item on page (to be set by template)
  40. var itemId = null;
  41.  
  42. // XMLHTTPRequest object to communicate with server.
  43. var iaXMLHTTP = null;
  44. if (window.ActiveXObject) {
  45.     try {
  46.         iaXMLHTTP = new ActiveXObject('Microsoft.XMLHTTP');
  47.     }
  48.     catch (e) { }
  49. }
  50. else if (window.XMLHttpRequest) {
  51.     iaXMLHTTP = new XMLHttpRequest();
  52. }
  53.  
  54. // Permissions (respect previous settings):
  55. // Allowed values are 'allow', 'prompt', 'deny'.
  56. if (!window.IA_ADD)    var IA_ADD = 'allow';
  57. if (!window.IA_MODIFY) var IA_MODIFY = 'allow';
  58. if (!window.IA_DELETE) var IA_DELETE = 'allow';
  59.  
  60. // Internationalisation:
  61. var IA_CREDITS = 'ImageAreas Module UI\n\n' +
  62.     'Based on Fotonotes DHTML viewer, (c) 2004-2005 Angus Turnbull, http://www.twinhelix.com\n\n';
  63. var IA_DISALLOWED = 'Sorry, that action is not permitted.\n\n' +
  64.     'Please login under a different account.';
  65. var IA_POST_UNSUPPORTED = 'Sorry, your browser does not support editing notes.';
  66. var IA_DELETE_CONFIRM = 'Are you sure you want to delete this the following note?';
  67. var IA_SAVE_WAIT = 'Loading ImageAreas module...';
  68. var IA_SAVE_FAIL = 'An error occurred, and your changes could not be saved.';
  69. var IA_LOAD_FAIL = 'An error occurred, and the ImageAreas module could not load.';
  70. var IA_SAVE_FAIL_JPEG_NOT_WRITABLE = "JPEG file is not writable." +
  71.     "Please check file permissions on server.";
  72. var IA_SAVE_SUCCESS = 'Changes saved!';
  73. var IA_MOVED_LINK = 'View image';
  74.  
  75. // Other global variables:
  76. var iaDebugMode     = false;   // Set to true to show XML sent/received.
  77. var iaHideTimer     = null;    // Hide notes after timeout.
  78. var iaActiveNote    = null;    // Currently visible note.
  79. var iaActionVerb    = '';      // Control bar's current action.
  80. var iaActionTrigger = null;    // Control bar's lit item.
  81. var iaEditingData   = null;    // Data store during note editing process.
  82. var iaMinImgWidth   = 300;     // MinWidth to make to apply to ia-image
  83. var iaMinImgHeight  = 300;     // MinHeight to make to apply to ia-image
  84.  
  85.  
  86. /**
  87. * Finds the image(s) in the DOM for which Fotonotes should be activated and does so.
  88. * Currently applies to all images greater than minimum height/width.
  89. * TODO: specifically find the main image on photo.tpl in the DOM
  90. * @param url url of server as generated by template
  91. * @param id  id of image we're loading for 
  92. */
  93. function loadImageAreas(url, id) {
  94.     iaServer = url;
  95.     itemId = id;
  96.     for (i=0;i < document.images.length; i++) {
  97.         if ( (document.images[i].width >= iaMinImgWidth)
  98.                 && (document.images[i].height >= iaMinImgHeight)) {     
  99.             var imgObj = document.images[i];
  100.             moveLinkBelow(imgObj, IA_MOVED_LINK);
  101.             getImageAreasUI(imgObj);
  102.         }
  103.     }   
  104. }       
  105.  
  106. /**
  107. * If the element is inside a link, remove it and place the link below the element.
  108. * @param elm       DOM element to remove from link
  109. * @param linkText  text to use for the moved link
  110. */
  111. function moveLinkBelow(elm, linkText) {
  112.     if (elm.parentNode.tagName == "A") {
  113.         var linkNode = elm.parentNode;
  114.         var origParent = linkNode.parentNode;
  115.         origParent.replaceChild(elm, linkNode);
  116.         linkNode.innerHTML = linkText;
  117.         origParent.appendChild(linkNode);
  118.     }
  119. }
  120.  
  121. /**
  122. * Sends some XML off to the server to get UI for image; calls iaGetClientComplete on
  123. * completion.
  124. * @param imgObj  reference to <img> element in the DOM
  125. */
  126. function getImageAreasUI(imgObj) {
  127.     var imageFile = imgObj.src;
  128.  
  129.     // Compose our post content and send it.
  130.     var postContent = 'g2_src=' + escape(imageFile) + '&g2_action=' + 'display'
  131.         + '&g2_itemId=' + itemId + '&g2_width=' + imgObj.width + '&g2_height=' + imgObj.height
  132.         + '&g2_alt=' + imgObj.alt;
  133.  
  134.     iaXMLHTTP.open('POST', iaServer, true);
  135.     iaXMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
  136.     iaXMLHTTP.setRequestHeader('Content-length', postContent.length);
  137.     iaXMLHTTP.onreadystatechange = function() {
  138.         if (iaXMLHTTP.readyState == 4) receiveImageAreasUI(true,imgObj,iaXMLHTTP.responseText);
  139.     };
  140.  
  141.     iaModalDialog(IA_SAVE_WAIT);
  142.     iaXMLHTTP.send(postContent);
  143. }
  144.  
  145. /**
  146. * All successful actions: let the user know it's OK, and reset the control bar,
  147. * and clear the editing data store.
  148. */
  149. function receiveImageAreasUI(ok,imgObj,responseText) {
  150.     setTimeout('iaModalDialog("")', 500);
  151.     // Called once the server responds post-Save operation. 'ok' indicates success.
  152.  
  153.     // EXTRACT returned HTML text from reply to update document
  154.     re = /displayHTML##([\w\W\n\r]*)##/;
  155.     matches = re.exec(responseText);
  156.     if(!matches || matches.length < 2) {
  157.         /* Something bad happened on the server side.  We lose. */
  158.         return;
  159.     }
  160.     var iaDiv = matches[1]// first matched pattern
  161.     iaDivElement = document.createElement('div');
  162.     iaDivElement.innerHTML = iaDiv;
  163.     imgObj.parentNode.insertBefore(iaDivElement,imgObj);
  164.     imgObj.parentNode.removeChild(imgObj);
  165. }
  166.  
  167. /*************
  168. * CORE CODE *
  169. *************/
  170.  
  171. /**
  172. * Fader function that shows/hides an element.
  173. * @param elm   DOM element to show or hide
  174. * @param show  true to fade in, false to fade out
  175. */
  176. function iaElementFade(elm, show) {
  177.     var speed = show ? 20 : 10;
  178.     elm._f_count |= 0;
  179.     elm._f_timer |= null;
  180.     clearTimeout(elm._f_timer);
  181.  
  182.     if (show && !elm._f_count) elm.style.visibility = 'inherit';
  183.  
  184.     elm._f_count = Math.max(0, Math.min(100, elm._f_count + speed*(show?1:-1)));
  185.  
  186.     var f = elm.filters, done = (elm._f_count==100);
  187.     if (f) {
  188.         if (!done && elm.style.filter.indexOf("alpha") == -1) {
  189.             elm.style.filter += ' alpha(opacity=' + elm._f_count + ')';
  190.         }
  191.         else if (f.length && f.alpha)  {
  192.             with (f.alpha) {
  193.                 if (done) enabled = false;
  194.                 else { opacity = elm._f_count; enabled=true }
  195.             }
  196.         }
  197.     }
  198.     else {
  199.         elm.style.opacity = elm.style.MozOpacity = elm._f_count/100.1;
  200.     }
  201.    
  202.     if (!show && !elm._f_count) { elm.style.visibility = 'hidden'; }
  203.  
  204.     if (elm._f_count % 100) {
  205.         elm._f_timer = setTimeout(function() { iaElementFade(elm,show) }, 50);
  206.     }
  207. }
  208.  
  209.  
  210. /**
  211. * Utility function that toggles the "-active" and "-inactive" classnames.
  212. * @param elm     DOM element to toggle
  213. * @param active  true for active, false for inactive
  214. */
  215. function iaClassSet(elm, active) {
  216.     elm.className = elm.className.replace((active ? (/-inactive/) : (/-active/)),
  217.                                           (active ? '-active' : '-inactive'));
  218. }
  219.  
  220. /**
  221. * When passed a DOM node, returns its parent "ia-container".
  222. * @param node DOM node
  223. */
  224. function iaGetContainer(node) {
  225.     var container = node;
  226.     while (container) {
  227.         if ((/ia-container/).test(container.className)) { break; }
  228.         container = container.parentNode;
  229.     }
  230.     return container;
  231. }
  232.  
  233.  
  234. /**
  235. * When passed a container, returns the control bar within that container.
  236. * @param container DOM reference to a container
  237. */
  238. function iaGetControlBar(container) {
  239.     var controlBar = null;
  240.     for (var i = 0; i < container.childNodes.length; i++) {
  241.         if ((/ia-controlbar/).test(container.childNodes.item(i).className)) {
  242.             controlBar = container.childNodes.item(i);
  243.             break;
  244.         }
  245.     }
  246.     return controlBar;
  247. };
  248.  
  249.  
  250. /**
  251. * Sets the "activated" status of a note container area, and changes
  252. * the appropriate "toggle" item in its control bar.
  253. */
  254. function iaContainerSet(container, active) {
  255.     var controlBar = iaGetControlBar(container);
  256.     for (var i = 0; i < controlBar.childNodes.length; i++) {
  257.         if ((/ia-controlbar-toggle/).test(controlBar.childNodes.item(i).className)) {
  258.             iaClassSet(controlBar.childNodes.item(i), !active);
  259.             break;
  260.         }
  261.     }
  262.     iaClassSet(container, active);
  263. }
  264.  
  265.  
  266. /**
  267. * Called on click of control buttons to highlight/dim them.
  268. */
  269. function iaAction(action, trigger) {
  270.     // Control the state of the trigger buttons, and set the global iaActionVerb variable.
  271.     if (iaActionVerb != action) {
  272.         // Set a new action, dim the old button.
  273.         if (iaActionTrigger && iaActionVerb) { iaClassSet(iaActionTrigger, false); }
  274.         iaActionVerb = action;
  275.         iaActionTrigger = trigger;
  276.         if (trigger) { iaClassSet(trigger, true); }
  277.     }
  278.     else {
  279.         // Deactivate a trigger that is clicked twice.
  280.         iaActionVerb = '';
  281.         if (trigger) { iaClassSet(trigger, false); }
  282.     }
  283. }
  284.  
  285.  
  286. /**
  287. * Called on document.onmouseover & onmouseout, manages tip visibility.
  288. */
  289. function iaMouseOverOutHandler(evt, isOver) {
  290.     var node = evt.target || evt.srcElement;
  291.     if (node.nodeType != 1) node = node.parentNode;
  292.  
  293.     while (node && !((node.className||'').indexOf('ia-container') > -1)) {
  294.         // If the node has an CLASS of "fotonote-area", process it.
  295.         // No mouseovers if iaActionVerb is set (i.e. editing/deleting/adding/etc).
  296.         if (node && ((node.className||'').indexOf('ia-area') > -1) && !iaActionVerb) {
  297.             var area = node;
  298.             // Find the first child element, which will be the note in question.
  299.             var note = area.firstChild;
  300.             while (note && note.nodeType != 1) note = note.nextSibling;
  301.             if (!note) return;
  302.  
  303.             // Clear any hide timeout, and either show the note, or set a timeout for its hide.
  304.             // We record the currently active note for the hide timer to work, and also elevate
  305.             // its parent area above any previously active area (which is lowered).
  306.             clearTimeout(iaHideTimer);
  307.             if (isOver) {
  308.                 if (iaActiveNote && (note != iaActiveNote)) { iaElementFade(iaActiveNote, false); }
  309.                 iaElementFade(note, true);
  310.                 if (iaActiveNote) { iaActiveNote.parentNode.style.zIndex = 1; }
  311.                 note.parentNode.style.zIndex = 2;
  312.                 iaActiveNote = note;
  313.             }
  314.             else {
  315.                 iaHideTimer = setTimeout('if (iaActiveNote) { ' +
  316.                     'iaElementFade(iaActiveNote, false); iaActiveNote = null }', 200);
  317.             }
  318.         }
  319.  
  320.         // Loop up the DOM.
  321.         node = node.parentNode;
  322.     }
  323. }
  324.  
  325.  
  326.  
  327. /**
  328. * Processes clicks on the document, performs the correct action.
  329. * @param evt event
  330. */
  331. function iaClickHandler(evt) {
  332.     var node = evt.target || evt.srcElement;
  333.     if (node.nodeType != 1) node = node.parentNode;
  334.     while (node && !((node.className||'').indexOf('ia-container') > -1)) {
  335.         // Check buttons within the Edit bar.
  336.         if ((/ia-editbar-ok/).test(node.className)) { return iaEditButtonHandler(true); }
  337.         if ((/ia-editbar-cancel/).test(node.className)) { return iaEditButtonHandler(false); }
  338.  
  339.         // Perform no other if we're currently editing a note.
  340.         if (iaEditingData) { return; }
  341.  
  342.         // If an existing area with a CLASS of the form "ia-area"
  343.         // has been clicked, check if we're editing/deleting it.
  344.         if ((/ia-area/).test(node.className)) {
  345.             var area = node;
  346.             if (iaActionVerb == 'del') { iaDelNote(area); }
  347.             if (iaActionVerb == 'edit') {
  348.                 var note = area.firstChild;
  349.                 while (note && note.nodeType != 1) note = note.nextSibling;
  350.                 if (note) iaEditNote(note);
  351.             }
  352.             return;
  353.         }
  354.  
  355.         // Buttons on/within the Control bar.
  356.         if ((/ia-controlbar-logo/).test(node.className)) {
  357.             // Logo click toggles control bar, if we're not editing a note.
  358.             var isActive = ((/ia-controlbar-active/).test(node.parentNode.className));
  359.             iaClassSet(node.parentNode, !isActive);
  360.             return;
  361.         }
  362.         if ((/ia-controlbar-credits/).test(node.className)) {
  363.             alert(IA_CREDITS);
  364.             return;
  365.         }
  366.         if ((/ia-controlbar-del/).test(node.className)) {
  367.             if (!iaXMLHTTP) { return alert(IA_POST_UNSUPPORTED); }
  368.             if (IA_DELETE == 'deny') { return alert(IA_DISALLOWED); }
  369.             return iaAction('del', node);
  370.         }
  371.         if ((/ia-controlbar-edit/).test(node.className)) {
  372.             if (!iaXMLHTTP) { return alert(IA_POST_UNSUPPORTED); }
  373.             if (IA_MODIFY == 'deny') { return alert(IA_DISALLOWED); }
  374.             return iaAction('edit', node);
  375.         }
  376.         if ((/ia-controlbar-add/).test(node.className)) {
  377.             if (!iaXMLHTTP) { return alert(IA_POST_UNSUPPORTED); }
  378.             if (IA_ADD == 'deny') { return alert(IA_DISALLOWED); }
  379.             return iaAddNote(node);
  380.         }
  381.         if ((/ia-controlbar-toggle/).test(node.className)) {
  382.             // Find the parent container, and toggle its classname to show/hide notes.
  383.             var container = iaGetContainer(node);
  384.             if (container) {
  385.                 var isActive = ((/ia-container-active/).test(container.className));
  386.                 iaContainerSet(container, !isActive);
  387.             }
  388.         }
  389.  
  390.         // Otherwise, loop up the hierarchy.
  391.         node = node.parentNode;
  392.     }
  393. }
  394.  
  395.  
  396. /*
  397. * Either shows or hides the editing UI.
  398. * @param show true to show, false to hide
  399. */
  400. function iaEditUISet(show) {
  401.     if (!iaEditingData) return;
  402.  
  403.     // Find our container and form references.
  404.     var container = iaGetContainer(area);
  405.     if (!container) { return; }
  406.     var form = container.getElementsByTagName('form');
  407.     if (!form) { return; }
  408.     form = form.item(0);
  409.  
  410.     with (iaEditingData) {
  411.         // Start or stop dragging the selected area.
  412.         if (show) { dragresize.select(area, area); }
  413.         else { dragresize.deselect(true); }
  414.  
  415.         // Set area className so its remains visible if editing, or reset it back otherwise.
  416.         area.className = show ? 'ia-area-editing' : 'ia-area';
  417.         // Fade the editing UI in/out, and toggle its classname so it stays that way.
  418.         iaElementFade(form, show);
  419.         iaClassSet(form, show);
  420.         // Toggle the container class and control bar (for other notes' visibility)
  421.         iaContainerSet(container, !show);
  422.         iaClassSet(iaGetControlBar(container), !show)
  423.     }
  424. }
  425.  
  426.  
  427. /**
  428. * Adds a new note when the specified button is clicked.
  429. * @param node
  430. */
  431. function iaAddNote(node) {
  432.     // Find the parent container of this node.
  433.     var container = iaGetContainer(node);
  434.     if (!container) return;
  435.  
  436.     // Highlight the "Add" button.
  437.     iaAction('add', node);
  438.  
  439.     // Create a new area in which the note will reside.
  440.     var newArea = document.createElement('div');
  441.     newArea.className = 'ia-area';
  442.     newArea.style.left = (container.offsetWidth/2 - 25) + 'px';
  443.     newArea.style.top  = (container.offsetHeight/2 - 25) + 'px';
  444.     newArea.style.width = '50px';
  445.     newArea.style.height = '50px';
  446.     newArea.className = newArea.className + ' ia-area-new';
  447.  
  448.     var newNote = document.createElement('div');
  449.     newNote.className = 'ia-note';
  450.     newArea.appendChild(newNote);
  451.  
  452.     // Create note elements.
  453.     var newTitle = document.createElement('span');
  454.     newTitle.className = 'ia-note-title';
  455.     newNote.appendChild(newTitle);
  456.  
  457.     var newContent = document.createElement('span');
  458.     newContent.className = 'ia-note-content';
  459.     newNote.appendChild(newContent);
  460.  
  461.     // add in innerborders
  462.     var newInnerBorder = document.createElement('div');
  463.     newInnerBorder.className = 'ia-area-innerborder-right';
  464.     newArea.appendChild(newInnerBorder);
  465.    
  466.     var newInnerBorder = document.createElement('div');
  467.     newInnerBorder.className = 'ia-area-innerborder-left';
  468.     newArea.appendChild(newInnerBorder);
  469.    
  470.     var newInnerBorder = document.createElement('div');
  471.     newInnerBorder.className = 'ia-area-innerborder-top';
  472.     newArea.appendChild(newInnerBorder);
  473.    
  474.     var newInnerBorder = document.createElement('div');
  475.     newInnerBorder.className = 'ia-area-innerborder-bottom';
  476.     newArea.appendChild(newInnerBorder);
  477.  
  478.     // Add newArea to document
  479.     container.appendChild(newArea);
  480.  
  481.     // Record this note as editing, and set the "add" action flag.
  482.     iaEditingData = {
  483.         area: newArea,
  484.         note: newNote
  485.     };
  486.  
  487.     // Hand over to the editing function.
  488.     iaEditNote();
  489. }
  490.  
  491.  
  492. /**
  493. * Edits a passed note reference.
  494. * @param note note to edit, if existing
  495. */
  496. function iaEditNote(note)
  497. {
  498.     var area = null;
  499.     if (note) {
  500.         // If we're editing an existing note, setup the data store.
  501.         area = note.parentNode;
  502.         iaEditingData = {
  503.             area: area,
  504.             note: note
  505.         };
  506.     }
  507.     else {
  508.         // New notes: pull the note and area out of the stored data.
  509.         area = iaEditingData.area;
  510.         note = iaEditingData.note;
  511.     }
  512.  
  513.     // Find our container and form references.
  514.     var container = iaGetContainer(area);
  515.     if (!container) { return; }
  516.     var form = container.getElementsByTagName('form');
  517.     if (!form) { return; }
  518.     form = form.item(0);
  519.    
  520.     // Pick up existing values for content from the note.
  521.     var oldTitle = '', oldAuthor = '', oldContent = '', noteID = '';
  522.     var fields = area.getElementsByTagName('span');
  523.     for (var n = 0; n < fields.length; n++) {
  524.         var field = fields.item(n);
  525.         if (field.className == 'ia-note-id') { noteID = field.getAttribute('title'); }
  526.         if (field.className == 'ia-note-title') { oldTitle = field.innerHTML; }
  527.         if (field.className == 'ia-note-author') { oldAuthor = field.innerHTML; }
  528.         if (field.className == 'ia-note-content') { oldContent = field.innerHTML; }
  529.     }
  530.  
  531.     // Backup the original content, refs and position in our datastore.
  532.     // It already has the .note and .area properties.
  533.     // And yes, I know innerHTML isn't standard, but it's SO MUCH EASIER here!
  534.     iaEditingData.container = container;
  535.     iaEditingData.form = form;
  536.     iaEditingData.noteID = noteID;
  537.     iaEditingData.oldTitle = oldTitle;
  538.     iaEditingData.oldAuthor = oldAuthor;
  539.     iaEditingData.oldContent = oldContent;
  540.     iaEditingData.oldLeft = parseInt(area.style.left);
  541.     iaEditingData.oldTop = parseInt(area.style.top);
  542.     iaEditingData.oldWidth = area.offsetWidth;
  543.     iaEditingData.oldHeight = area.offsetHeight;
  544.     // Some values for the post-editing callback handler to populate.
  545.     iaEditingData.newTitle = iaEditingData.newAuthor = iaEditingData.newContent = '';
  546.     iaEditingData.newLeft = iaEditingData.newTop = 0;
  547.     iaEditingData.newWidth = iaEditingData.newHeight = 0;
  548.  
  549.     // Populate the editing UI with its current content.
  550.     var inputs = form.getElementsByTagName('input');
  551.     for (var i = 0; i < inputs.length; i++) {
  552.         if ((/title/).test(inputs[i].className)) inputs[i].value = oldTitle;
  553.         if ((/author/).test(inputs[i].className)) inputs[i].value = oldAuthor;
  554.     }
  555.     var textarea = form.getElementsByTagName('textarea');
  556.     if (textarea && (/content/).test(textarea.item(0).className)) {
  557.         textarea.item(0).value = oldContent;
  558.     }
  559.  
  560.     // Finally, show the editing UI for the recorded area.
  561.     iaEditUISet(true);
  562. }
  563.  
  564.  
  565. /**
  566. * Returns a properly escaped HTML string.
  567. * @param html HTML string to escape
  568. */
  569. function iaEscapeHTML(html) {
  570.     return html.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;');
  571. }
  572.  
  573.  
  574. /**
  575. * Button click handler from the editing UI.
  576. * @param ok Pass a boolean value indicating if the OK button was clicked (so save should proceed).
  577. */
  578. function iaEditButtonHandler(ok) {
  579.     if (!iaEditingData) { return; }
  580.     with (iaEditingData) {
  581.         if (ok) {
  582.             // Populate iaEditingData.new* from the edit form fields and area attributes.
  583.             // SET default value for all params.
  584.             newTitle = newAuthor = newUserid = newEntryid = newContent = newBorderColor = '';
  585.                 var inputs = form.getElementsByTagName('input');
  586.             for (var i = 0; i < inputs.length; i++) {
  587.                 if ((/title/).test(inputs[i].className)) {newTitle = inputs[i].value;}
  588.                 if ((/author/).test(inputs[i].className)) {newAuthor = inputs[i].value;}
  589.                 if ((/userid/).test(inputs[i].className)) {newUserid = inputs[i].value;}
  590.                 if ((/entry_id/).test(inputs[i].className)) {newEntryid = inputs[i].value;}
  591.                 if ((/border_color/).test(inputs[i].className)) {newBorderColor = inputs[i].value;}
  592.             }
  593.             var textarea = form.getElementsByTagName('textarea');
  594.             if (textarea && (/content/).test(textarea.item(0).className)) {newContent = textarea.item(0).value};
  595.             newLeft = parseInt(area.style.left);
  596.             newTop = parseInt(area.style.top);
  597.             newWidth = area.offsetWidth;
  598.             newHeight = area.offsetHeight;
  599.                     
  600.             if (iaDebugMode) {
  601.                 alert('Begin server save operation ' + 'newBorderColor: ' + newBorderColor);
  602.             }
  603.             
  604.             // Get the scalefactor from a hidden SPAN in the container.
  605.             var sFact = 1;
  606.             for (var n = 0; n < container.childNodes.length; n++) {
  607.                 if ((/ia-scalefactor/).test(container.childNodes.item(n).className))
  608.                     sFact = parseFloat(container.childNodes.item(n).getAttribute('title'));
  609.             }
  610.             
  611.             // Begin server save operation.
  612.             var data = {  'areaId'      : (iaActionVerb == 'edit' ? noteID : ''),
  613.                           'itemId'      : itemId,
  614.                           'areaTitle'   : iaEscapeHTML(newTitle),
  615.                           'areaContent' : iaEscapeHTML(newContent),
  616.                           'areaBounds'  : parseInt(newLeft/sFact) + ','
  617.                                           + parseInt(newTop/sFact) + ','
  618.                                           + parseInt((newLeft+newWidth)/sFact) + ','
  619.                                           + parseInt((newTop+newHeight)/sFact)
  620.                        }
  621.  
  622.             iaPostJSON(data);
  623.         }
  624.         else {
  625.             // For "cancel" clicks:
  626.             if (iaActionVerb == 'add') {
  627.                 // Just delete new notes.
  628.                 area.parentNode.removeChild(area);
  629.             }
  630.             else {
  631.                 // Restore original note area position/size for edited notes.
  632.                 area.style.left = oldLeft + 'px';
  633.                 area.style.top = oldTop + 'px';
  634.                 area.style.width = oldWidth + 'px';
  635.                 area.style.height = oldHeight + 'px';
  636.             }
  637.  
  638.             // Hide the editing UI, reset the control bar, clear the data store.
  639.             iaEditUISet(false);
  640.             iaAction('', null);
  641.             iaEditingData = null;
  642.         }
  643.     }
  644.  
  645. }
  646.  
  647. /**
  648. * Deletes a note area
  649. * @param area a whole area reference
  650. */
  651. function iaDelNote(area)
  652. {
  653.     // Find the ID of this note.
  654.     var areaID = iaGetAreaID(area);
  655.     if (!areaID) { alert(IA_SAVE_FAIL); }
  656.  
  657.     area.className += ' ia-area-highlight';
  658.  
  659.     var areaTitle = iaGetAreaTitle(area);
  660.     var areaContent = iaGetAreaContent(area);
  661.     if (areaID && confirm(IA_DELETE_CONFIRM  + '\n\n' + areaTitle + '\n' + areaContent)) {
  662.         // Set up our data store to delete this area, and post to the server.
  663.         iaEditingData = {
  664.             area: area,
  665.             note: null,
  666.             container: iaGetContainer(area)
  667.         };
  668.         iaPostJSON( { 'areaId' : areaID } );
  669.     }
  670.     else {
  671.         // Reset control bar if cancelled.
  672.         area.className = 'ia-area';
  673.         iaAction('', null);
  674.     }
  675. }
  676.  
  677. /**
  678. * Given an area, returns the areaId data stored in the XHTML.
  679. */
  680. function iaGetAreaID(area) {
  681.     fields = area.getElementsByTagName('span');
  682.     for (var n = 0; n < fields.length; n++) {
  683.         if (fields.item(n).className == 'ia-note-id') {
  684.             return fields.item(n).getAttribute('title');
  685.         }
  686.     }
  687.     return null;
  688. }
  689.  
  690. /**
  691. * Given an area, returns the title data stored in the XHTML.
  692. */
  693. function iaGetAreaTitle(area) {
  694.     fields = area.getElementsByTagName('span');
  695.     for (var n = 0; n < fields.length; n++) {
  696.         if (fields.item(n).className == 'ia-note-title') {
  697.             return fields.item(n).innerHTML;
  698.         }
  699.     }
  700.     return null;
  701. }
  702.  
  703. /**
  704. * Given an area, returns the content data stored in the XHTML.
  705. */
  706. function iaGetAreaContent(area) {
  707.     fields = area.getElementsByTagName('span');
  708.     for (var n = 0; n < fields.length; n++) {
  709.         if (fields.item(n).className == 'ia-note-content') {
  710.             return fields.item(n).innerHTML;
  711.         }
  712.     }
  713.     return null;
  714. }
  715.  
  716.  
  717. /**
  718. * Shows or hides the browser-wide modal dialog.
  719. * @param message Pass a message to show, or an empty string to hide the dialog.
  720. */
  721. function iaModalDialog(message) {
  722.     var dialog = document.getElementById('ia-modaldialog');
  723.     if (!dialog) {
  724.         dialog = document.createElement('div');
  725.         dialog.setAttribute('id', 'ia-modaldialog');
  726.         document.body.appendChild(dialog);
  727.     }
  728.  
  729.     dialog.innerHTML = '<span>' + message + '</span>';
  730.     dialog.style.visibility = message ? 'visible' : 'hidden';
  731. }
  732.  
  733.  
  734.  
  735.  
  736. /**
  737. * Sends some JSON off to the server and calls iaEditComplete on completion.
  738. * @param data object to send
  739. */
  740. function iaPostJSON(data) {
  741.     // Hopefully my auto-detect-fu powers are strong. I'll use the Crouching Regex Style.
  742.     var image = iaEditingData.container.getElementsByTagName('img').item(0);
  743.     var imageFile = image.getAttribute('src');
  744.     if (!imageFile) return alert(IA_SAVE_FAIL);
  745.  
  746.     // Compose our post content and send it.
  747.     var actVerbs = { add: 'add', edit: 'modify', del: 'delete' };