Dramatically improved the page-rename UX and added a miniPrompt API that allows small pop-down prompts as opposed to prompt() or confirm().
authorDan
Tue, 08 Apr 2008 20:08:35 -0400
changeset 522 fd46b1bf708e
parent 519 94214ec0871c
child 523 9d239396be42
Dramatically improved the page-rename UX and added a miniPrompt API that allows small pop-down prompts as opposed to prompt() or confirm().
images/prompt-body.png
images/prompt-bottom.png
images/prompt-top.png
includes/clientside/css/enano-shared.css
includes/clientside/static/ajax.js
includes/clientside/static/faders.js
language/english/core.json
Binary file images/prompt-body.png has changed
Binary file images/prompt-bottom.png has changed
Binary file images/prompt-top.png has changed
--- a/includes/clientside/css/enano-shared.css	Sun Apr 06 15:30:39 2008 -0400
+++ b/includes/clientside/css/enano-shared.css	Tue Apr 08 20:08:35 2008 -0400
@@ -745,3 +745,40 @@
   background-image: url(../../../images/expander/open-prelight.gif);
 }
 
+/* Flown-in mini prompts */
+
+div.miniprompt {
+  position: absolute;
+}
+
+div.miniprompt div.mp-top, div.miniprompt div.mp-bottom {
+  width: 388px;
+  height: 57px;
+  background-image: url(../../../images/prompt-top.png);
+  background-repeat: no-repeat;
+}
+
+div.miniprompt div.mp-bottom {
+  height: 67px;
+  background-image: url(../../../images/prompt-bottom.png);
+}
+
+div.miniprompt div.mp-body {
+  padding: 0 44px 10px 44px;
+  width: 300px;
+  background-image: url(../../../images/prompt-body.png);
+  background-repeat: repeat-y;
+  background-position: center center;
+}
+
+div.miniprompt h3 {
+  /* fix padding issues on firefox */
+  margin: 0 0 10px 0;
+}
+
+/* for buttons */
+div.miniprompt div.mp-buttons {
+  text-align: right;
+  position: relative;
+  top: 10px;
+}
--- a/includes/clientside/static/ajax.js	Sun Apr 06 15:30:39 2008 -0400
+++ b/includes/clientside/static/ajax.js	Tue Apr 08 20:08:35 2008 -0400
@@ -239,15 +239,138 @@
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
-  r = prompt($lang.get('ajax_rename_prompt'));
-  if(!r || r=='') return;
-  setAjaxLoading();
-  ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+ajaxEscape(r), function() {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      alert(ajax.responseText);
-    }
-  }, true);
+  
+  // updated - 1.1.4 to use miniPrompt
+  miniPrompt(ajaxRenameConstructDialog);
+}
+
+var ajaxRenameConstructDialog = function(div)
+{
+  // title
+  var heading = document.createElement('h3');
+  heading.appendChild(document.createTextNode($lang.get('ajax_rename_prompt_short')));
+  div.appendChild(heading);
+  
+  // form
+  var form = document.createElement('form');
+  form.action = 'javascript:void(0);';
+  
+  // box
+  var box = document.createElement('input');
+  box.size = '43';
+  box.style.width = '100%';
+  form.appendChild(box);
+  div.appendChild(form);
+  
+  // notice
+  var notice = document.createElement('small');
+  notice.appendChild(document.createTextNode($lang.get('ajax_rename_notice')));
+  div.appendChild(notice);
+  
+  // button area
+  var btndiv = document.createElement('div');
+  btndiv.className = 'mp-buttons';
+  
+  // buttons
+  var btn_submit = document.createElement('a');
+  btn_submit.href = '#';
+  btn_submit.appendChild(document.createTextNode($lang.get('etc_go')));
+  btn_submit.className = 'fakebutton fakebutton-submit';
+  
+  var btn_cancel = document.createElement('a');
+  btn_cancel.href = '#';
+  btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
+  btn_cancel.className = 'fakebutton';
+  
+  btndiv.appendChild(btn_submit);
+  btndiv.appendChild(document.createTextNode(' | '));
+  btndiv.appendChild(btn_cancel);
+  div.appendChild(btndiv);
+  
+  // events
+  btn_submit.onclick = function()
+  {
+    ajaxRenameSubmit(this);
+    return false;
+  }
+  btn_cancel.onclick = function()
+  {
+    miniPromptDestroy(this);
+    return false;
+  }
+  form.onsubmit = function()
+  {
+    ajaxRenameSubmit(this);
+    return false;
+  }
+  
+  setTimeout(function()
+    {
+      box.focus();
+    }, 200);
+}
+
+function ajaxRenameSubmit(obj)
+{
+  var box = miniPromptGetParent(obj);
+  if ( !box )
+    return false;
+  
+  var newname = ( obj.getElementsByTagName('input')[0] ).value;
+  newname = trim(newname);
+  
+  if ( newname.length < 1 )
+  {
+    alert($lang.get('ajax_rename_too_short'));
+    return false;
+  }
+  
+  if ( !newname )
+  {
+    return false;
+  }
+  
+  var innerBox = getElementsByClassName(box, 'div', 'mp-body')[0];
+  var whiteout = whiteOutElement(innerBox);
+  whiteout.style.width = ( $(whiteout).Width() - 78 ) + 'px';
+  whiteout.style.left = ( $(whiteout).Left() + 44 ) + 'px';
+  
+  ajaxPost(stdAjaxPrefix + '&_mode=rename', 'newtitle=' + ajaxEscape(newname), function()
+    {
+      if ( ajax.readyState == 4 && ajax.status == 200 )
+      {
+        whiteout.parentNode.removeChild(whiteout);
+        var response = String(ajax.responseText);
+        if ( response.substr(0, 1) != '{' )
+        {
+          handle_invalid_json(response);
+          return false;
+        }
+        response = parseJSON(response);
+        if ( response.success )
+        {
+          miniPromptDestroy(box, true);
+          ajaxRenameDoClientTransform(newname);
+          new messagebox( MB_OK|MB_ICONINFORMATION, $lang.get('ajax_rename_success_title'), $lang.get('ajax_rename_success_body', { page_name_new: newname }) );
+          mb_previously_had_darkener = false;
+        }
+        else
+        {
+          var errmsg = $lang.get('page_err_' + response.error);
+          alert(errmsg);
+        }
+      }
+    }, true);
+}
+
+function ajaxRenameDoClientTransform(newname)
+{
+  var obj = document.getElementById('h2PageName');
+  if ( obj )
+  {
+    obj.firstChild.nodeValue = newname;
+  }
+  document.title = newname;
 }
 
 function ajaxMakePage()
--- a/includes/clientside/static/faders.js	Sun Apr 06 15:30:39 2008 -0400
+++ b/includes/clientside/static/faders.js	Tue Apr 08 20:08:35 2008 -0400
@@ -1,20 +1,39 @@
-// Message box system
+// Message box and visual effect system
 
-function darken(nofade)
+/**
+ * Darkens the browser screen. This will make the entire page un-clickable except for any floating divs created after this is called. Restore with enlighten().
+ * @param bool Controls whether the fade should be disabled or not. aclDisableTransitionFX will override this if set to true, and fades are never fired on IE.
+ * @param int When specified, represents the numeric opacity value to set the fade layer to. 1-100.
+ */
+
+function darken(nofade, opacVal)
 {
   if(IE)
     nofade = true;
+  if ( !opacVal )
+    opacVal = 70;
   if(document.getElementById('specialLayer_darkener'))
   {
     if(nofade)
     {
-      changeOpac(70, 'specialLayer_darkener');
+      changeOpac(opacVal, 'specialLayer_darkener');
       document.getElementById('specialLayer_darkener').style.display = 'block';
+      document.getElementById('specialLayer_darkener').myOpacVal = opacVal;
     }
     else
     {
-      document.getElementById('specialLayer_darkener').style.display = 'block';
-      opacity('specialLayer_darkener', 0, 70, 1000);
+      if ( document.getElementById('specialLayer_darkener').style.display != 'none' )
+      {
+        var currentOpac = document.getElementById('specialLayer_darkener').myOpacVal;
+        opacity('specialLayer_darkener', currentOpac, opacVal, 1000);
+        document.getElementById('specialLayer_darkener').myOpacVal = opacVal;
+      }
+      else
+      {
+        document.getElementById('specialLayer_darkener').style.display = 'block';
+        document.getElementById('specialLayer_darkener').myOpacVal = opacVal;
+        opacity('specialLayer_darkener', 0, opacVal, 1000);
+      }
     }
   } else {
     w = getWidth();
@@ -41,10 +60,11 @@
     thediv.style.height = '100%';
     thediv.zIndex = getHighestZ() + 5;
     thediv.id = 'specialLayer_darkener';
+    thediv.myOpacVal = opacVal;
     if(nofade)
     {
-      thediv.style.opacity = '0.7';
-      thediv.style.filter = 'alpha(opacity=70)';
+      thediv.style.opacity = ( parseFloat(opacVal) / 100 );
+      thediv.style.filter = 'alpha(opacity=' + opacVal + ')';
       body = document.getElementsByTagName('body');
       body = body[0];
       body.appendChild(thediv);
@@ -52,11 +72,16 @@
       body = document.getElementsByTagName('body');
       body = body[0];
       body.appendChild(thediv);
-      opacity('specialLayer_darkener', 0, 70, 1000);
+      opacity('specialLayer_darkener', 0, opacVal, 1000);
     }
   }
 }
 
+/**
+ * Un-darkens the screen and re-enables clicking of on-screen controls.
+ * @param bool If true, disables the fade effect. Fades are always disabled if aclDisableTransitionFX is true and on IE.
+ */
+
 function enlighten(nofade)
 {
   if(IE)
@@ -67,8 +92,13 @@
     {
       document.getElementById('specialLayer_darkener').style.display = 'none';
     }
-    opacity('specialLayer_darkener', 70, 0, 1000);
-    setTimeout("document.getElementById('specialLayer_darkener').style.display = 'none';", 1000);
+    else
+    {
+      var from = document.getElementById('specialLayer_darkener').myOpacVal;
+      // console.info('Fading from ' + from);
+      opacity('specialLayer_darkener', from, 0, 1000);
+      setTimeout("document.getElementById('specialLayer_darkener').style.display = 'none';", 1000);
+    }
   }
 }
 
@@ -388,6 +418,102 @@
     }
 }
 
+/**
+ * The miniPrompt function, for creating small prompts and dialogs. The window will be flown in and the window darkened with opac=0.4.
+ * @param function Will be passed an HTMLElement that is the body of the prompt window; the function can do with this as it pleases
+ */
+
+function miniPrompt(call_on_create)
+{
+  darken(false, 40);
+  
+  var wrapper = document.createElement('div');
+  wrapper.className = 'miniprompt';
+  var top = document.createElement('div');
+  top.className = 'mp-top';
+  var body = document.createElement('div');
+  body.className = 'mp-body';
+  var bottom = document.createElement('div');
+  bottom.className = 'mp-bottom';
+  if ( typeof(call_on_create) == 'function' )
+  {
+    call_on_create(body);
+  }
+  wrapper.appendChild(top);
+  wrapper.appendChild(body);
+  wrapper.appendChild(bottom);
+  var left = ( getWidth() / 2 ) - ( 388 / 2 );
+  wrapper.style.left = left + 'px';
+  var top = getScrollOffset() - 27;
+  wrapper.style.top = top + 'px';
+  domObjChangeOpac(0, wrapper);
+  var realbody = document.getElementsByTagName('body')[0];
+  realbody.appendChild(wrapper);
+  
+  fly_in_top(wrapper, true, true);
+  
+  setTimeout(function()
+    {
+      domObjChangeOpac(100, wrapper);
+    }, 40);
+}
+
+/**
+ * For a given element, loops through the element and all of its ancestors looking for a miniPrompt div, and returns it. Returns false on failure.
+ * @param object:HTMLElement Child node to scan
+ * @return object
+ */
+
+function miniPromptGetParent(obj)
+{
+  while ( true )
+  {
+    // prevent infinite loops
+    if ( !obj || obj.tagName == 'BODY' )
+      return false;
+    
+    if ( $dynano(obj).hasClass('miniprompt') )
+    {
+      return obj;
+    }
+    obj = obj.parentNode;
+  }
+  return false;
+}
+
+/**
+ * Destroys the first miniPrompt div encountered by recursively checking all parent nodes.
+ * Usage: <a href="javascript:miniPromptDestroy(this);">click</a>
+ * @param object:HTMLElement a child of the div.miniprompt
+ * @param bool If true, does not call enlighten().
+ */
+
+function miniPromptDestroy(obj, nofade)
+{
+  obj = miniPromptGetParent(obj);
+  if ( !obj )
+    return false;
+  
+  // found it
+  var parent = obj.parentNode;
+  if ( !nofade )
+    enlighten();
+  var timeout = fly_out_top(obj, true, true);
+  setTimeout(function()
+    {
+      parent.removeChild(obj);
+    }, timeout);
+}
+
+/**
+ * Simple test case
+ */
+
+function miniPromptTest()
+{
+  miniPrompt(function(div) { div.innerHTML = 'hello world! <a href="#" onclick="miniPromptDestroy(this); return false;">destroy me</a>'; });
+}
+
 // Function to fade classes info-box, warning-box, error-box, etc.
 
 function fadeInfoBoxes()
--- a/language/english/core.json	Sun Apr 06 15:30:39 2008 -0400
+++ b/language/english/core.json	Tue Apr 08 20:08:35 2008 -0400
@@ -430,7 +430,9 @@
     ajax: {
       // Client-side messages
       protect_prompt_reason: 'Reason for (un)protecting:',
-      rename_prompt: 'What title should this page be renamed to?\nNote: This does not and will never change the URL of this page, that must be done from the admin panel.',
+      rename_prompt: 'What title should this page be renamed to?\n%this.ajax_rename_notice%',
+      rename_prompt_short: 'Enter a new name for this page',
+      rename_notice: 'This won\'t change the URL to this page. To change the URL to this page, use Page Manager in the administration panel.',
       delete_prompt_reason: 'Please enter your reason for deleting this page.',
       delete_confirm: 'You are about to REVERSIBLY delete this page. Do you REALLY want to do this?\n\n(Comments and categorization data, as well as any attached files, will be permanently lost)',
       delvote_confirm: 'Are you sure that you want to vote that this page be deleted?',
@@ -449,7 +451,8 @@
       
       // Server-side responses
       rename_too_short: 'The name you entered is too short. Please enter a longer name for this page.',
-      rename_success: 'The page "%page_name_old%" has been renamed to "%page_name_new%". You are encouraged to leave a comment explaining your action.\n\nYou will see the change take effect the next time you reload this page.',
+      rename_success_title: 'Page renamed',
+      rename_success_body: 'This page has been renamed to "%page_name_new%". You are encouraged to leave a comment explaining your action.<br /><br />Please note that you might not see this change take effect until you reload the page.',
       clearlogs_success: 'The logs for this page have been cleared. A backup of this page has been added to the logs table so that this page can be restored in case of vandalism or spam later.',
       delete_need_reason: 'Invalid reason for deletion passed. Please enter a reason for deleting this page.',
       delete_success: 'This page has been deleted. Note that there is still a log of edits and actions in the database, and anyone with admin rights can raise this page from the dead unless the log is cleared. If the deleted file is an image, there may still be cached thumbnails of it in the cache/ directory, which is inaccessible to users.',
@@ -458,7 +461,6 @@
       delvote_reset_success: 'The number of votes for having this page deleted has been reset to zero.',
       password_success: 'The password for this page has been set.',
       password_disable_success: 'The password for this page has been disabled.',
-      
     },
     sidebar: {
       title_navigation: 'Navigation',
@@ -691,6 +693,7 @@
       cancel: 'Cancel',
       yes: 'Yes',
       no: 'No',
+      go: 'Go',
       unit_bytes: 'bytes',
       unit_kilobytes: 'kilobytes',
       unit_megabytes: 'megabytes',