MegaAlex has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/98137


Change subject: Merge jquery.placeholder with 
github.com/mathiasbynens/jquery-placeholder
......................................................................

Merge jquery.placeholder with github.com/mathiasbynens/jquery-placeholder

New features added:
* Support for valHooks and propHooks
* Clearing placeholders upon for submission
* jquery.placeholder QUnit test suite

Bug: 57179
Change-Id: I1ecca3d89bb964414ad8765caaa7e1958b94f762
---
M resources/jquery/jquery.placeholder.js
M tests/qunit/QUnitTestResources.php
A tests/qunit/suites/resources/jquery/jquery.placeholder.test.js
3 files changed, 347 insertions(+), 71 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/37/98137/1

diff --git a/resources/jquery/jquery.placeholder.js 
b/resources/jquery/jquery.placeholder.js
index abada19..934bac6 100644
--- a/resources/jquery/jquery.placeholder.js
+++ b/resources/jquery/jquery.placeholder.js
@@ -5,90 +5,216 @@
  *
  * @author Trevor Parscal <[email protected]>, 2012
  * @author Krinkle <[email protected]>, 2012
- * @version 0.2.0
+ * @author Mathias Bynens <http://mathiasbynens.be/>
+ * @author Alex Ivanov <[email protected]>, 2013
+ * @version 0.3.0
  * @license MIT
  */
-( function ( $ ) {
+(function(window, document, $) {
 
-       $.fn.placeholder = function ( text ) {
-               var hasArg = arguments.length;
+       var isInputSupported = 'placeholder' in document.createElement('input');
+       var isTextareaSupported = 'placeholder' in 
document.createElement('textarea');
+       var prototype = $.fn;
+       var valHooks = $.valHooks;
+       var propHooks = $.propHooks;
+       var hooks;
+       var placeholder;
 
-               return this.each( function () {
-                       var placeholder, $input;
+       if (isInputSupported && isTextareaSupported) {
 
-                       if ( hasArg ) {
-                               this.setAttribute( 'placeholder', text );
+               placeholder = prototype.placeholder = function( text ) {
+                       var hasArgs = arguments.length;
+
+                       if( hasArgs ) {
+                               changePlaceholder.call(this, text);
                        }
 
-                       // If the HTML5 placeholder attribute is supported, use 
it
-                       if ( this.placeholder && 'placeholder' in 
document.createElement( this.tagName ) ) {
-                               return;
+                       return this;
+               };
+
+               placeholder.input = placeholder.textarea = true;
+
+       } else {
+
+               placeholder = prototype.placeholder = function( text ) {
+                       var $this = this;
+                       var hasArgs = arguments.length;
+
+                       if( hasArgs ) {
+                               changePlaceholder.call(this, text);
                        }
 
-                       placeholder = hasArg ? text : this.getAttribute( 
'placeholder' );
-                       $input = $(this);
 
-                       // Show initially, if empty
-                       if ( this.value === '' || this.value === placeholder ) {
-                               $input.addClass( 'placeholder' ).val( 
placeholder );
-                       }
+                       $this
+                               .filter((isInputSupported ? 'textarea' : 
':input') + '[placeholder]')
+                               .not('.placeholder-enabled')
+                               .bind({
+                                       'focus.placeholder drop.placeholder': 
clearPlaceholder,
+                                       'blur.placeholder': setPlaceholder
+                               })
+                               .addClass('placeholder-enabled')
+                               .trigger('blur.placeholder');
+                       return $this;
+               };
 
-                       $input
-                               // Show on blur if empty
-                               .blur( function () {
-                                       if ( this.value === '' ) {
-                                               this.value = placeholder;
-                                               $input.addClass( 'placeholder' 
);
+               placeholder.input = isInputSupported;
+               placeholder.textarea = isTextareaSupported;
+
+               hooks = {
+                       'get': function(element) {
+                               var $element = $(element);
+
+                               var $passwordInput = 
$element.data('placeholder-password');
+                               if ($passwordInput) {
+                                       return $passwordInput[0].value;
+                               }
+
+                               return $element.hasClass('placeholder-enabled') 
&& $element.hasClass('placeholder') ? '' : element.value;
+                       },
+                       'set': function(element, value) {
+                               var $element = $(element);
+
+                               var $passwordInput = 
$element.data('placeholder-password');
+                               if ($passwordInput) {
+                                       return $passwordInput[0].value = value;
+                               }
+
+                               if (!$element.hasClass('placeholder-enabled')) {
+                                       return element.value = value;
+                               }
+                               if (value == '') {
+                                       element.value = value;
+                                       // Issue #56: Setting the placeholder 
causes problems if the element continues to have focus.
+                                       if (element != safeActiveElement()) {
+                                               // We can't use 
`triggerHandler` here because of dummy text/password inputs :(
+                                               setPlaceholder.call(element);
                                        }
-                               } )
-
-                               // Hide on focus
-                               // Also listen for other events in case $input 
was
-                               // already focused when the events were bound
-                               .on( 'focus drop keydown paste', function ( e ) 
{
-                                       if ( $input.hasClass( 'placeholder' ) ) 
{
-                                               if ( e.type === 'drop' && 
e.originalEvent.dataTransfer ) {
-                                                       // Support for 
drag&drop. Instead of inserting the dropped
-                                                       // text somewhere in 
the middle of the placeholder string,
-                                                       // we want to set the 
contents of the search box to the
-                                                       // dropped text.
-
-                                                       // IE wants getData( 
'text' ) but Firefox wants getData( 'text/plain' )
-                                                       // Firefox fails 
gracefully with an empty string, IE barfs with an error
-                                                       try {
-                                                               // Try the 
Firefox way
-                                                               this.value = 
e.originalEvent.dataTransfer.getData( 'text/plain' );
-                                                       } catch ( exception ) {
-                                                               // Got an 
exception, so use the IE way
-                                                               this.value = 
e.originalEvent.dataTransfer.getData( 'text' );
-                                                       }
-
-                                                       // On Firefox, drop 
fires after the dropped text has been inserted,
-                                                       // but on IE it fires 
before. If we don't prevent the default action,
-                                                       // IE will insert the 
dropped text twice.
-                                                       e.preventDefault();
-                                               } else {
-                                                       this.value = '';
-                                               }
-                                               $input.removeClass( 
'placeholder' );
-                                       }
-                               } );
-
-                       // Blank on submit -- prevents submitting with 
unintended value
-                       if ( this.form ) {
-                               $( this.form ).submit( function () {
-                                       // $input.trigger( 'focus' ); would be 
problematic
-                                       // because it actually focuses $input, 
leading
-                                       // to nasty behavior in mobile browsers
-                                       if ( $input.hasClass( 'placeholder' ) ) 
{
-                                               $input
-                                                       .val( '' )
-                                                       .removeClass( 
'placeholder' );
-                                       }
-                               });
+                               } else if ($element.hasClass('placeholder')) {
+                                       clearPlaceholder.call(element, true, 
value) || (element.value = value);
+                               } else {
+                                       element.value = value;
+                               }
+                               // `set` can not return `undefined`; see 
http://jsapi.info/jquery/1.7.1/val#L2363
+                               return $element;
                        }
+               };
 
+               if (!isInputSupported) {
+                       valHooks.input = hooks;
+                       propHooks.value = hooks;
+               }
+               if (!isTextareaSupported) {
+                       valHooks.textarea = hooks;
+                       propHooks.value = hooks;
+               }
+
+               $(function() {
+                       // Look for forms
+                       $(document).delegate('form', 'submit.placeholder', 
function() {
+                               // Clear the placeholder values so they don't 
get submitted
+                               var $inputs = $('.placeholder', 
this).each(clearPlaceholder);
+                               setTimeout(function() {
+                                       $inputs.each(setPlaceholder);
+                               }, 10);
+                       });
                });
-       };
 
-}( jQuery ) );
+               // Clear placeholder values upon page reload
+               $(window).bind('beforeunload.placeholder', function() {
+                       $('.placeholder').each(function() {
+                               this.value = '';
+                       });
+               });
+
+       }
+
+       function args(elem) {
+               // Return an object of element attributes
+               var newAttrs = {};
+               var rinlinejQuery = /^jQuery\d+$/;
+               $.each(elem.attributes, function(i, attr) {
+                       if (attr.specified && !rinlinejQuery.test(attr.name)) {
+                               newAttrs[attr.name] = attr.value;
+                       }
+               });
+               return newAttrs;
+       }
+
+       function clearPlaceholder(event, value) {
+               var input = this;
+               var $input = $(input);
+               if (input.value == $input.attr('placeholder') && 
$input.hasClass('placeholder')) {
+                       if ($input.data('placeholder-password')) {
+                               $input = $input.hide().next().show().attr('id', 
$input.removeAttr('id').data('placeholder-id'));
+                               // If `clearPlaceholder` was called from 
`$.valHooks.input.set`
+                               if (event === true) {
+                                       return $input[0].value = value;
+                               }
+                               $input.focus();
+                       } else {
+                               input.value = '';
+                               $input.removeClass('placeholder');
+                               input == safeActiveElement() && input.select();
+                       }
+               }
+       }
+
+       function setPlaceholder() {
+               var $replacement;
+               var input = this;
+               var $input = $(input);
+               var id = this.id;
+               if (input.value == '') {
+                       if (input.type == 'password') {
+                               if (!$input.data('placeholder-textinput')) {
+                                       try {
+                                               $replacement = 
$input.clone().attr({ 'type': 'text' });
+                                       } catch(e) {
+                                               $replacement = 
$('<input>').attr($.extend(args(this), { 'type': 'text' }));
+                                       }
+                                       $replacement
+                                               .removeAttr('name')
+                                               .data({
+                                                       'placeholder-password': 
$input,
+                                                       'placeholder-id': id
+                                               })
+                                               .bind('focus.placeholder 
drop.placeholder', clearPlaceholder);
+                                       $input
+                                               .data({
+                                                       
'placeholder-textinput': $replacement,
+                                                       'placeholder-id': id
+                                               })
+                                               .before($replacement);
+                               }
+                               $input = 
$input.removeAttr('id').hide().prev().attr('id', id).show();
+                               // Note: `$input[0] != input` now!
+                       }
+                       $input.addClass('placeholder');
+                       $input[0].value = $input.attr('placeholder');
+               } else {
+                       $input.removeClass('placeholder');
+               }
+       }
+
+       function safeActiveElement() {
+               // Avoid IE9 `document.activeElement` of death
+               // https://github.com/mathiasbynens/jquery-placeholder/pull/99
+               try {
+                       return document.activeElement;
+               } catch (err) {}
+       }
+
+       function changePlaceholder( text ) {
+               var hasArgs = arguments.length;
+               var $input = this;
+               if( hasArgs ) {
+                       if($input.attr('placeholder') != text) {
+                               $input.prop('placeholder', text);
+                               if($input.hasClass('placeholder')) {
+                                       $input[0].value = text;
+                               }
+                       }
+               }
+       }
+
+}(this, document, jQuery));
diff --git a/tests/qunit/QUnitTestResources.php 
b/tests/qunit/QUnitTestResources.php
index c874375..01fedc8 100644
--- a/tests/qunit/QUnitTestResources.php
+++ b/tests/qunit/QUnitTestResources.php
@@ -19,6 +19,7 @@
                        
'tests/qunit/suites/resources/jquery/jquery.localize.test.js',
                        
'tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js',
                        
'tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js',
+                       
'tests/qunit/suites/resources/jquery/jquery.placeholder.test.js',
                        
'tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js',
                        
'tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js',
                        
'tests/qunit/suites/resources/jquery/jquery.textSelection.test.js',
@@ -49,6 +50,7 @@
                        'jquery.localize',
                        'jquery.makeCollapsible',
                        'jquery.mwExtension',
+                       'jquery.placeholder',
                        'jquery.tabIndex',
                        'jquery.tablesorter',
                        'jquery.textSelection',
diff --git a/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js 
b/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js
new file mode 100644
index 0000000..1b7f07b
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js
@@ -0,0 +1,148 @@
+(function($) {
+
+       QUnit.module( 'jquery.placeholder', QUnit.newMwEnvironment() );
+
+       QUnit.test('caches results of feature tests', 2, function( assert ) {
+               assert.strictEqual(typeof $.fn.placeholder.input, 'boolean', 
'$.fn.placeholder.input');
+               assert.strictEqual(typeof $.fn.placeholder.textarea, 'boolean', 
'$.fn.placeholder.textarea');
+       });
+
+       if ($.fn.placeholder.input && $.fn.placeholder.textarea) {
+               return;
+       }
+
+       html = '<form>' +
+                       '<input id="input-type-search" type="search" 
placeholder="Search this site...">' +
+                       '<input id="input-type-text" type="text" 
placeholder="e.g. John Doe">' +
+                       '<input id="input-type-email" type="email" 
placeholder="e.g. [email protected]">' +
+                       '<input id="input-type-url" type="url" 
placeholder="e.g. http://mathiasbynens.be/";>' +
+                       '<input id="input-type-tel" type="tel" 
placeholder="e.g. +32 472 77 69 88">' +
+                       '<input id="input-type-password" type="password" 
placeholder="e.g. hunter2">' +
+                       '<textarea id="textarea" name="message" 
placeholder="Your message goes here"></textarea>' +
+               '</form>';
+
+       var testElement = function($el, assert) {
+
+               var el = $el[0];
+               var placeholder = el.getAttribute('placeholder');
+
+               assert.strictEqual($el.placeholder(), $el, 'should be 
chainable');
+
+               assert.strictEqual(el.value, placeholder, 'should set 
`placeholder` text as `value`');
+               assert.strictEqual($el.prop('value'), '', 'propHooks works 
properly');
+               assert.strictEqual($el.val(), '', 'valHooks works properly');
+               assert.ok($el.hasClass('placeholder'), 'should have 
`placeholder` class');
+
+               // test on focus
+               $el.focus();
+               assert.strictEqual(el.value, '', '`value` should be the empty 
string on focus');
+               assert.strictEqual($el.prop('value'), '', 'propHooks works 
properly');
+               assert.strictEqual($el.val(), '', 'valHooks works properly');
+               assert.ok(!$el.hasClass('placeholder'), 'should not have 
`placeholder` class on focus');
+
+               // and unfocus (blur) again
+               $el.blur();
+
+               assert.strictEqual(el.value, placeholder, 'should set 
`placeholder` text as `value`');
+               assert.strictEqual($el.prop('value'), '', 'propHooks works 
properly');
+               assert.strictEqual($el.val(), '', 'valHooks works properly');
+               assert.ok($el.hasClass('placeholder'), 'should have 
`placeholder` class');
+
+               // change the value
+               $el.val('lorem ipsum');
+               assert.strictEqual($el.prop('value'), 'lorem ipsum', 
'`$el.val(string)` should change the `value` property');
+               assert.strictEqual(el.value, 'lorem ipsum', '`$el.val(string)` 
should change the `value` attribute');
+               assert.ok(!$el.hasClass('placeholder'), '`$el.val(string)` 
should remove `placeholder` class');
+
+               // and clear it again
+               $el.val('');
+               assert.strictEqual($el.prop('value'), '', '`$el.val("")` should 
change the `value` property');
+               assert.strictEqual(el.value, placeholder, '`$el.val("")` should 
change the `value` attribute');
+               assert.ok($el.hasClass('placeholder'), '`$el.val("")` should 
re-enable `placeholder` class');
+
+               // make sure the placeholder property works as expected.
+               assert.strictEqual($el.prop('placeholder'), placeholder, 
'$el.prop(`placeholder`) should return the placeholder value');
+               $el.placeholder('new placeholder');
+               assert.strictEqual(el.getAttribute('placeholder'), 'new 
placeholder', '$el.placeholder(<string>) should set the placeholder value');
+               assert.strictEqual(el.value, 'new placeholder', 
'$el.placeholder(<string>) should update the displayed placeholder value');
+               $el.placeholder(placeholder);
+       };
+
+       QUnit.test('emulates placeholder for <input type=text>', 22, function( 
assert ) {
+               $( '<div>' ).html( html ).appendTo( $('#qunit-fixture') );
+               testElement( $('#input-type-text'), assert );
+       });
+
+       QUnit.test('emulates placeholder for <input type=search>', 22, 
function( assert ) {
+               $( '<div>' ).html( html ).appendTo( $('#qunit-fixture') );
+               testElement( $('#input-type-search'), assert );
+       });
+
+       QUnit.test('emulates placeholder for <input type=email>', 22, function( 
assert ) {
+               $( '<div>' ).html( html ).appendTo( $('#qunit-fixture') );
+               testElement( $('#input-type-email'), assert );
+       });
+
+       QUnit.test('emulates placeholder for <input type=url>', 22, function( 
assert ) {
+               $( '<div>' ).html( html ).appendTo( $('#qunit-fixture') );
+               testElement( $('#input-type-url'), assert );
+       });
+
+       QUnit.test('emulates placeholder for <input type=tel>', 22, function( 
assert ) {
+               $( '<div>' ).html( html ).appendTo( $('#qunit-fixture') );
+               testElement( $('#input-type-tel'), assert );
+       });
+
+       QUnit.test('emulates placeholder for <input type=password>', 13, 
function( assert ) {
+               $( '<div>' ).html( html ).appendTo( $('#qunit-fixture') );
+
+               var selector = '#input-type-password';
+
+               var $el = $(selector);
+               var el = $el[0];
+
+               var placeholder = el.getAttribute('placeholder');
+
+               assert.strictEqual($el.placeholder(), $el, 'should be 
chainable');
+
+               // Re-select the element, as it gets replaced by another one in 
some browsers
+               $el = $(selector);
+               el = $el[0];
+
+               assert.strictEqual(el.value, placeholder, 'should set 
`placeholder` text as `value`');
+               assert.strictEqual($el.prop('value'), '', 'propHooks works 
properly');
+               assert.strictEqual($el.val(), '', 'valHooks works properly');
+               assert.ok($el.hasClass('placeholder'), 'should have 
`placeholder` class');
+
+               // test on focus
+               $el.focus();
+
+               // Re-select the element, as it gets replaced by another one in 
some browsers
+               $el = $(selector);
+               el = $el[0];
+
+               assert.strictEqual(el.value, '', '`value` should be the empty 
string on focus');
+               assert.strictEqual($el.prop('value'), '', 'propHooks works 
properly');
+               assert.strictEqual($el.val(), '', 'valHooks works properly');
+               assert.ok(!$el.hasClass('placeholder'), 'should not have 
`placeholder` class on focus');
+
+               // and unfocus (blur) again
+               $el.blur();
+
+               // Re-select the element, as it gets replaced by another one in 
some browsers
+               $el = $(selector);
+               el = $el[0];
+
+               assert.strictEqual(el.value, placeholder, 'should set 
`placeholder` text as `value`');
+               assert.strictEqual($el.prop('value'), '', 'propHooks works 
properly');
+               assert.strictEqual($el.val(), '', 'valHooks works properly');
+               assert.ok($el.hasClass('placeholder'), 'should have 
`placeholder` class');
+
+       });
+
+       QUnit.test('emulates placeholder for <textarea></textarea>', 22, 
function( assert ) {
+               $( '<div>' ).html( html ).appendTo( $('#qunit-fixture') );
+               testElement( $('#textarea'), assert );
+       });
+
+}(jQuery));

-- 
To view, visit https://gerrit.wikimedia.org/r/98137
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I1ecca3d89bb964414ad8765caaa7e1958b94f762
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: MegaAlex <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to