From 7177d3a4963ab43a6b24e4f1676bff5d74fc0a52 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 6 May 2014 12:07:53 +0200 Subject: [PATCH 01/11] first step of infield label removal, fix login screen --- core/css/styles.css | 35 ++++++++++++++--------------------- core/templates/login.php | 20 +++++++++++--------- lib/base.php | 1 - 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index a9ffd83629..2fd2ad36ab 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -464,19 +464,19 @@ input[name='password-clone'] { width: 223px !important; padding-left: 36px !important; } -#adminlogin+label+img, +#adminlogin~img, #adminpass-icon, -#user+label+img, +#user~img, #password-icon { position: absolute; - left: 1.25em; - top: 1.65em; + left: 16px; + top: 24px; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter: alpha(opacity=30); opacity: .3; } #adminpass-icon, #password-icon { - top: 1.1em; + top: 18px; } /* General new input field look */ @@ -489,6 +489,11 @@ input[name='password-clone'] { } /* Nicely grouping input field sets */ +.grouptop, +.groupmiddle, +.groupbottom { + position: relative; +} #body-login .grouptop input { margin-bottom: 0; border-bottom: 0; @@ -511,23 +516,11 @@ input[name='password-clone'] { box-shadow: 0 1px 0 rgba(0,0,0,.1) inset !important; } -/* In field labels. No, HTML placeholder does not work as well. */ -#body-login .groupmiddle label, #body-login .groupbottom label { top:.65em; } -p.infield { position:relative; } -label.infield { cursor:text !important; top:1.05em; left:.85em; } -#body-login form label.infield { /* labels are ellipsized when too long, keep them short */ - position: absolute; - width: 82%; - margin-left: 26px; - font-size: 19px; - color: #aaa; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -#body-login #databaseField .infield { - margin-left: 0; +/* keep the labels for screen readers but hide them since we use placeholders */ +label.infield { + display: none; } + #body-login form input[type="checkbox"]+label { position: relative; margin: 0; diff --git a/core/templates/login.php b/core/templates/login.php index 0f25f853b0..6af3d76969 100644 --- a/core/templates/login.php +++ b/core/templates/login.php @@ -26,19 +26,21 @@

-

- - autocomplete="on" autocapitalize="off" autocorrect="off" required /> +

+ + autocomplete="on" autocapitalize="off" autocorrect="off" required />

-

- - autocomplete="on" autocapitalize="off" autocorrect="off" required /> +

+ + autocomplete="on" autocapitalize="off" autocorrect="off" required />

diff --git a/lib/base.php b/lib/base.php index 5f2131f388..160d346a01 100644 --- a/lib/base.php +++ b/lib/base.php @@ -320,7 +320,6 @@ class OC { OC_Util::addScript("jquery-migrate-1.2.1.min"); OC_Util::addScript("jquery-ui-1.10.0.custom"); OC_Util::addScript("jquery-showpassword"); - OC_Util::addScript("jquery.infieldlabel"); OC_Util::addScript("jquery.placeholder"); OC_Util::addScript("jquery-tipsy"); OC_Util::addScript("compatibility"); From 04aa08529257b5c981603b50743818fab8f064a9 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 6 May 2014 12:14:11 +0200 Subject: [PATCH 02/11] infield label removal: fix installation screen --- core/css/styles.css | 2 +- core/templates/installation.php | 35 ++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 2fd2ad36ab..39ba541fca 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -578,7 +578,7 @@ label.infield { } #show + label, #dbpassword + label, #personal-show + label { position: absolute !important; - height: 14px; + height: 20px; width: 24px; background-image: url("../img/actions/toggle.png"); background-repeat: no-repeat; diff --git a/core/templates/installation.php b/core/templates/installation.php index 6d73fb431f..f934e3a86c 100644 --- a/core/templates/installation.php +++ b/core/templates/installation.php @@ -46,15 +46,17 @@
t( 'Create an admin account' )); ?> -

- +

-

- + @@ -105,40 +107,45 @@

-

+

-

-

- +

-

+

-

-

+

-

-

+

-

From 4ca665ac10978216be91b58f395b0dd00f4b9540 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 6 May 2014 12:30:43 +0200 Subject: [PATCH 03/11] infield label removal: remove JS file and references to it --- core/js/core.json | 1 - core/js/jquery.infieldlabel.js | 177 --------------------------------- core/js/js.js | 5 - 3 files changed, 183 deletions(-) delete mode 100644 core/js/jquery.infieldlabel.js diff --git a/core/js/core.json b/core/js/core.json index f1e0ba883d..4815116c33 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -4,7 +4,6 @@ "jquery-migrate-1.2.1.min.js", "jquery-ui-1.10.0.custom.js", "jquery-showpassword.js", - "jquery.infieldlabel.js", "jquery.placeholder.js", "jquery-tipsy.js", "underscore.js" diff --git a/core/js/jquery.infieldlabel.js b/core/js/jquery.infieldlabel.js deleted file mode 100644 index fad15102bc..0000000000 --- a/core/js/jquery.infieldlabel.js +++ /dev/null @@ -1,177 +0,0 @@ -/* - * jquery.infieldlabel - * A simple jQuery plugin for adding labels that sit over a form field and fade away when the fields are populated. - * - * Copyright (c) 2009 - 2013 Doug Neiner (http://code.dougneiner.com) - * Source: https://github.com/dcneiner/In-Field-Labels-jQuery-Plugin - * Dual licensed MIT or GPL - * MIT (http://www.opensource.org/licenses/mit-license) - * GPL (http://www.opensource.org/licenses/gpl-license) - * - * @version 0.1.3 - */ -(function ($) { - - $.InFieldLabels = function (label, field, options) { - // To avoid scope issues, use 'base' instead of 'this' - // to reference this class from internal events and functions. - var base = this; - - // Access to jQuery and DOM versions of each element - base.$label = $(label); - base.label = label; - - base.$field = $(field); - base.field = field; - - base.$label.data("InFieldLabels", base); - base.showing = true; - - base.init = function () { - var initialSet; - - // Merge supplied options with default options - base.options = $.extend({}, $.InFieldLabels.defaultOptions, options); - - // Check if the field is already filled in - // add a short delay to handle autocomplete - setTimeout(function() { - if (base.$field.val() !== "") { - base.$label.hide(); - base.showing = false; - } else { - base.$label.show(); - base.showing = true; - } - }, 200); - - base.$field.focus(function () { - base.fadeOnFocus(); - }).blur(function () { - base.checkForEmpty(true); - }).bind('keydown.infieldlabel', function (e) { - // Use of a namespace (.infieldlabel) allows us to - // unbind just this method later - base.hideOnChange(e); - }).bind('paste', function () { - // Since you can not paste an empty string we can assume - // that the fieldis not empty and the label can be cleared. - base.setOpacity(0.0); - }).change(function () { - base.checkForEmpty(); - }).bind('onPropertyChange', function () { - base.checkForEmpty(); - }).bind('keyup.infieldlabel', function () { - base.checkForEmpty(); - }); - - if ( base.options.pollDuration > 0 ) { - initialSet = setInterval( function () { - if (base.$field.val() !== "") { - base.$label.hide(); - base.showing = false; - clearInterval( initialSet ); - } - }, base.options.pollDuration ); - - } - }; - - // If the label is currently showing - // then fade it down to the amount - // specified in the settings - base.fadeOnFocus = function () { - if (base.showing) { - base.setOpacity(base.options.fadeOpacity); - } - }; - - base.setOpacity = function (opacity) { - base.$label.stop().animate({ opacity: opacity }, base.options.fadeDuration); - base.showing = (opacity > 0.0); - }; - - // Checks for empty as a fail safe - // set blur to true when passing from - // the blur event - base.checkForEmpty = function (blur) { - if (base.$field.val() === "") { - base.prepForShow(); - base.setOpacity(blur ? 1.0 : base.options.fadeOpacity); - } else { - base.setOpacity(0.0); - } - }; - - base.prepForShow = function () { - if (!base.showing) { - // Prepare for a animate in... - base.$label.css({opacity: 0.0}).show(); - - // Reattach the keydown event - base.$field.bind('keydown.infieldlabel', function (e) { - base.hideOnChange(e); - }); - } - }; - - base.hideOnChange = function (e) { - if ( - (e.keyCode === 16) || // Skip Shift - (e.keyCode === 9) // Skip Tab - ) { - return; - } - - if (base.showing) { - base.$label.hide(); - base.showing = false; - } - - // Remove keydown event to save on CPU processing - base.$field.unbind('keydown.infieldlabel'); - }; - - // Run the initialization method - base.init(); - }; - - $.InFieldLabels.defaultOptions = { - fadeOpacity: 0.5, // Once a field has focus, how transparent should the label be - fadeDuration: 300, // How long should it take to animate from 1.0 opacity to the fadeOpacity - pollDuration: 0, // If set to a number greater than zero, this will poll until content is detected in a field - enabledInputTypes: [ "text", "search", "tel", "url", "email", "password", "number", "textarea" ] - }; - - - $.fn.inFieldLabels = function (options) { - var allowed_types = options && options.enabledInputTypes || $.InFieldLabels.defaultOptions.enabledInputTypes; - - return this.each(function () { - // Find input or textarea based on for= attribute - // The for attribute on the label must contain the ID - // of the input or textarea element - var for_attr = $(this).attr('for'), field, restrict_type; - if (!for_attr) { - return; // Nothing to attach, since the for field wasn't used - } - - // Find the referenced input or textarea element - field = document.getElementById( for_attr ); - if ( !field ) { - return; // No element found - } - - // Restrict input type - restrict_type = $.inArray( field.type, allowed_types ); - - if ( restrict_type === -1 && field.nodeName !== "TEXTAREA" ) { - return; // Again, nothing to attach - } - - // Only create object for matched input types and textarea - (new $.InFieldLabels(this, field, options)); - }); - }; - -}(jQuery)); diff --git a/core/js/js.js b/core/js/js.js index 1c4fb98c60..834916b2e3 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -1039,11 +1039,6 @@ function initCore() { setShowPassword($('#pass2'), $('label[for=personal-show]')); setShowPassword($('#dbpass'), $('label[for=dbpassword]')); - //use infield labels - $("label.infield").inFieldLabels({ - pollDuration: 100 - }); - var checkShowCredentials = function() { var empty = false; $('input#user, input#password').each(function() { From 4ef94ddd2eb87afe4cd8a0de3b70f32bbd9328fc Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 6 May 2014 12:43:52 +0200 Subject: [PATCH 04/11] infield label removal: remove last occurences of infield labels --- apps/files_sharing/css/authenticate.css | 7 ------- apps/files_sharing/templates/authenticate.php | 4 ++-- core/css/fixes.css | 4 ---- core/lostpassword/templates/lostpassword.php | 6 ++++-- core/lostpassword/templates/resetpassword.php | 6 ++++-- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/apps/files_sharing/css/authenticate.css b/apps/files_sharing/css/authenticate.css index ef963ba7c6..a5aa55632b 100644 --- a/apps/files_sharing/css/authenticate.css +++ b/apps/files_sharing/css/authenticate.css @@ -1,10 +1,3 @@ -#body-login form label.infield { - width: 190px; - padding: 10px; - left: 8px; - top: 8px; -} - #password { width: 190px !important; padding: 10px; diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php index 055329ecab..f899a54e0f 100644 --- a/apps/files_sharing/templates/authenticate.php +++ b/apps/files_sharing/templates/authenticate.php @@ -6,10 +6,10 @@
t('The password is wrong. Try again.')); ?>
-

+

diff --git a/core/css/fixes.css b/core/css/fixes.css index 0f18f0a56c..fc122b1eff 100644 --- a/core/css/fixes.css +++ b/core/css/fixes.css @@ -42,10 +42,6 @@ select { border-bottom: 1px solid lightgrey; background-color: white; /* don't change background on hover */ } -.lte9 #body-login form label.infield { - background-color: white; /* don't change background on hover */ - -ms-filter: "progid:DXImageTransform.Microsoft.Chroma(color='white')"; -} /* disable opacity of info text on gradient since we cannot set a good backround color to use the filter&background hack as with the input labels */ diff --git a/core/lostpassword/templates/lostpassword.php b/core/lostpassword/templates/lostpassword.php index d0fed38ee2..fdfa32344e 100644 --- a/core/lostpassword/templates/lostpassword.php +++ b/core/lostpassword/templates/lostpassword.php @@ -16,8 +16,10 @@ OCP\Util::addStyle('lostpassword', 'lostpassword');

t('You will receive a link to reset your password via Email.')); ?>
-

- +

+ diff --git a/core/lostpassword/templates/resetpassword.php b/core/lostpassword/templates/resetpassword.php index 881455f5a9..11dce9f112 100644 --- a/core/lostpassword/templates/resetpassword.php +++ b/core/lostpassword/templates/resetpassword.php @@ -4,9 +4,11 @@

t('Your password was reset')); ?>

t('To login page')); ?>

-

+

- +

From 46fd40b7000b0d5c25655780c7e4e27c341fa0eb Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 6 May 2014 12:44:16 +0200 Subject: [PATCH 05/11] fix show password icon in personal settings --- core/css/styles.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 39ba541fca..4c57f000ba 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -592,8 +592,9 @@ label.infield { width: 8em; } #personal-show + label { - margin-top: 1em; - margin-left: -3em; + height: 14px; + margin-top: 14px; + margin-left: -36px; } #passwordbutton { margin-left: .5em; From a63c3311ca524915fbf67304b35029fdf5b4833a Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Tue, 6 May 2014 13:50:03 +0200 Subject: [PATCH 06/11] remove scruntinizer config for infield --- .scrutinizer.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index d1dbb20139..3d0099ced0 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -14,7 +14,6 @@ filter: - 'core/js/jquery-migrate-1.2.1.min.js' - 'core/js/jquery-showpassword.js' - 'core/js/jquery-tipsy.js' - - 'core/js/jquery.infieldlabel.js' - 'core/js/jquery-ui-1.10.0.custom.js' - 'core/js/jquery.inview.js' - 'core/js/jquery.placeholder.js' From b67c4b2441d1ba7909f62668ac06bdbbc9f8ce4b Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Mon, 2 Jun 2014 23:39:27 +0200 Subject: [PATCH 07/11] remove tilde selector for IE8 compatibility --- core/css/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 4c57f000ba..3b05a16afc 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -464,9 +464,9 @@ input[name='password-clone'] { width: 223px !important; padding-left: 36px !important; } -#adminlogin~img, +#adminlogin+label+img, #adminpass-icon, -#user~img, +#user+label+img, #password-icon { position: absolute; left: 16px; From 9396b22fe3ecfa5862a59c59383b69c0ab337bc4 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Mon, 2 Jun 2014 23:39:59 +0200 Subject: [PATCH 08/11] add missing placeholder shim for change password fields in IE8/9 --- settings/js/personal.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/settings/js/personal.js b/settings/js/personal.js index f56dd3425f..8aeca71c29 100644 --- a/settings/js/personal.js +++ b/settings/js/personal.js @@ -187,6 +187,8 @@ $(document).ready(function(){ }); + $('.personalblock input[type=password]').placeholder(); + $('#displayName').keyUpDelayedOrEnter(changeDisplayName); $('#email').keyUpDelayedOrEnter(changeEmailAddress); From eace692effe8a6f2f7b645d5d1320da3e45e8643 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 3 Jun 2014 09:58:17 +0200 Subject: [PATCH 09/11] fix icon alignment in input fields --- core/css/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 3b05a16afc..7d8f12b7d2 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -470,13 +470,13 @@ input[name='password-clone'] { #password-icon { position: absolute; left: 16px; - top: 24px; + top: 20px; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter: alpha(opacity=30); opacity: .3; } #adminpass-icon, #password-icon { - top: 18px; + top: 15px; } /* General new input field look */ From 8a6063ff076cba636f4049298d00cd592f2dbdb6 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 3 Jun 2014 10:02:11 +0200 Subject: [PATCH 10/11] fix text alignment in input fields --- core/css/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/css/styles.css b/core/css/styles.css index 7d8f12b7d2..bd688f0ada 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -679,7 +679,7 @@ label.infield { #body-login input { font-size: 20px; margin: 5px; - padding: 12px 10px 8px; + padding: 11px 10px 9px; } #body-login input[type="text"], #body-login input[type="password"] { From cea7d4961ecb1a75de291a24b503a1e82e558e1b Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Tue, 3 Jun 2014 16:18:06 +0200 Subject: [PATCH 11/11] move to updated version of placeholder --- .scrutinizer.yml | 6 +- core/js/placeholders.js | 459 ++++++++++++++++++++++++++++++++ core/templates/layout.base.php | 12 +- core/templates/layout.guest.php | 12 +- core/templates/layout.user.php | 12 +- lib/base.php | 2 +- 6 files changed, 481 insertions(+), 22 deletions(-) create mode 100644 core/js/placeholders.js diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 3d0099ced0..bbffc9ce2a 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -15,12 +15,12 @@ filter: - 'core/js/jquery-showpassword.js' - 'core/js/jquery-tipsy.js' - 'core/js/jquery-ui-1.10.0.custom.js' - - 'core/js/jquery.inview.js' - - 'core/js/jquery.placeholder.js' + - 'core/js/jquery.inview.js' + - 'core/js/placeholders.js' - 'core/js/underscore.js' - 'core/js/jquery.multiselect.js' - + imports: - javascript - php diff --git a/core/js/placeholders.js b/core/js/placeholders.js new file mode 100644 index 0000000000..e63f429d40 --- /dev/null +++ b/core/js/placeholders.js @@ -0,0 +1,459 @@ +/* + * The MIT License + * + * Copyright (c) 2012 James Allardice + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Defines the global Placeholders object along with various utility methods +(function (global) { + + "use strict"; + + // Cross-browser DOM event binding + function addEventListener(elem, event, fn) { + if (elem.addEventListener) { + return elem.addEventListener(event, fn, false); + } + if (elem.attachEvent) { + return elem.attachEvent("on" + event, fn); + } + } + + // Check whether an item is in an array (we don't use Array.prototype.indexOf so we don't clobber any existing polyfills - this is a really simple alternative) + function inArray(arr, item) { + var i, len; + for (i = 0, len = arr.length; i < len; i++) { + if (arr[i] === item) { + return true; + } + } + return false; + } + + // Move the caret to the index position specified. Assumes that the element has focus + function moveCaret(elem, index) { + var range; + if (elem.createTextRange) { + range = elem.createTextRange(); + range.move("character", index); + range.select(); + } else if (elem.selectionStart) { + elem.focus(); + elem.setSelectionRange(index, index); + } + } + + // Attempt to change the type property of an input element + function changeType(elem, type) { + try { + elem.type = type; + return true; + } catch (e) { + // You can't change input type in IE8 and below + return false; + } + } + + // Expose public methods + global.Placeholders = { + Utils: { + addEventListener: addEventListener, + inArray: inArray, + moveCaret: moveCaret, + changeType: changeType + } + }; + +}(this)); + +(function (global) { + + "use strict"; + + var validTypes = [ + "text", + "search", + "url", + "tel", + "email", + "password", + "number", + "textarea" + ], + + // The list of keycodes that are not allowed when the polyfill is configured to hide-on-input + badKeys = [ + + // The following keys all cause the caret to jump to the end of the input value + 27, // Escape + 33, // Page up + 34, // Page down + 35, // End + 36, // Home + + // Arrow keys allow you to move the caret manually, which should be prevented when the placeholder is visible + 37, // Left + 38, // Up + 39, // Right + 40, // Down + + // The following keys allow you to modify the placeholder text by removing characters, which should be prevented when the placeholder is visible + 8, // Backspace + 46 // Delete + ], + + // Styling variables + placeholderStyleColor = "#ccc", + placeholderClassName = "placeholdersjs", + classNameRegExp = new RegExp("(?:^|\\s)" + placeholderClassName + "(?!\\S)"), + + // These will hold references to all elements that can be affected. NodeList objects are live, so we only need to get those references once + inputs, textareas, + + // The various data-* attributes used by the polyfill + ATTR_CURRENT_VAL = "data-placeholder-value", + ATTR_ACTIVE = "data-placeholder-active", + ATTR_INPUT_TYPE = "data-placeholder-type", + ATTR_FORM_HANDLED = "data-placeholder-submit", + ATTR_EVENTS_BOUND = "data-placeholder-bound", + ATTR_OPTION_FOCUS = "data-placeholder-focus", + ATTR_OPTION_LIVE = "data-placeholder-live", + ATTR_MAXLENGTH = "data-placeholder-maxlength", + + // Various other variables used throughout the rest of the script + test = document.createElement("input"), + head = document.getElementsByTagName("head")[0], + root = document.documentElement, + Placeholders = global.Placeholders, + Utils = Placeholders.Utils, + hideOnInput, liveUpdates, keydownVal, styleElem, styleRules, placeholder, timer, form, elem, len, i; + + // No-op (used in place of public methods when native support is detected) + function noop() {} + + // Avoid IE9 activeElement of death when an iframe is used. + // More info: + // http://bugs.jquery.com/ticket/13393 + // https://github.com/jquery/jquery/commit/85fc5878b3c6af73f42d61eedf73013e7faae408 + function safeActiveElement() { + try { + return document.activeElement; + } catch (err) {} + } + + // Hide the placeholder value on a single element. Returns true if the placeholder was hidden and false if it was not (because it wasn't visible in the first place) + function hidePlaceholder(elem, keydownValue) { + var type, + maxLength, + valueChanged = (!!keydownValue && elem.value !== keydownValue), + isPlaceholderValue = (elem.value === elem.getAttribute(ATTR_CURRENT_VAL)); + + if ((valueChanged || isPlaceholderValue) && elem.getAttribute(ATTR_ACTIVE) === "true") { + elem.removeAttribute(ATTR_ACTIVE); + elem.value = elem.value.replace(elem.getAttribute(ATTR_CURRENT_VAL), ""); + elem.className = elem.className.replace(classNameRegExp, ""); + + // Restore the maxlength value + maxLength = elem.getAttribute(ATTR_MAXLENGTH); + if (parseInt(maxLength, 10) >= 0) { // Old FF returns -1 if attribute not set (see GH-56) + elem.setAttribute("maxLength", maxLength); + elem.removeAttribute(ATTR_MAXLENGTH); + } + + // If the polyfill has changed the type of the element we need to change it back + type = elem.getAttribute(ATTR_INPUT_TYPE); + if (type) { + elem.type = type; + } + return true; + } + return false; + } + + // Show the placeholder value on a single element. Returns true if the placeholder was shown and false if it was not (because it was already visible) + function showPlaceholder(elem) { + var type, + maxLength, + val = elem.getAttribute(ATTR_CURRENT_VAL); + if (elem.value === "" && val) { + elem.setAttribute(ATTR_ACTIVE, "true"); + elem.value = val; + elem.className += " " + placeholderClassName; + + // Store and remove the maxlength value + maxLength = elem.getAttribute(ATTR_MAXLENGTH); + if (!maxLength) { + elem.setAttribute(ATTR_MAXLENGTH, elem.maxLength); + elem.removeAttribute("maxLength"); + } + + // If the type of element needs to change, change it (e.g. password inputs) + type = elem.getAttribute(ATTR_INPUT_TYPE); + if (type) { + elem.type = "text"; + } else if (elem.type === "password") { + if (Utils.changeType(elem, "text")) { + elem.setAttribute(ATTR_INPUT_TYPE, "password"); + } + } + return true; + } + return false; + } + + function handleElem(node, callback) { + + var handleInputsLength, handleTextareasLength, handleInputs, handleTextareas, elem, len, i; + + // Check if the passed in node is an input/textarea (in which case it can't have any affected descendants) + if (node && node.getAttribute(ATTR_CURRENT_VAL)) { + callback(node); + } else { + + // If an element was passed in, get all affected descendants. Otherwise, get all affected elements in document + handleInputs = node ? node.getElementsByTagName("input") : inputs; + handleTextareas = node ? node.getElementsByTagName("textarea") : textareas; + + handleInputsLength = handleInputs ? handleInputs.length : 0; + handleTextareasLength = handleTextareas ? handleTextareas.length : 0; + + // Run the callback for each element + for (i = 0, len = handleInputsLength + handleTextareasLength; i < len; i++) { + elem = i < handleInputsLength ? handleInputs[i] : handleTextareas[i - handleInputsLength]; + callback(elem); + } + } + } + + // Return all affected elements to their normal state (remove placeholder value if present) + function disablePlaceholders(node) { + handleElem(node, hidePlaceholder); + } + + // Show the placeholder value on all appropriate elements + function enablePlaceholders(node) { + handleElem(node, showPlaceholder); + } + + // Returns a function that is used as a focus event handler + function makeFocusHandler(elem) { + return function () { + + // Only hide the placeholder value if the (default) hide-on-focus behaviour is enabled + if (hideOnInput && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") { + + // Move the caret to the start of the input (this mimics the behaviour of all browsers that do not hide the placeholder on focus) + Utils.moveCaret(elem, 0); + + } else { + + // Remove the placeholder + hidePlaceholder(elem); + } + }; + } + + // Returns a function that is used as a blur event handler + function makeBlurHandler(elem) { + return function () { + showPlaceholder(elem); + }; + } + + // Functions that are used as a event handlers when the hide-on-input behaviour has been activated - very basic implementation of the "input" event + function makeKeydownHandler(elem) { + return function (e) { + keydownVal = elem.value; + + //Prevent the use of the arrow keys (try to keep the cursor before the placeholder) + if (elem.getAttribute(ATTR_ACTIVE) === "true") { + if (keydownVal === elem.getAttribute(ATTR_CURRENT_VAL) && Utils.inArray(badKeys, e.keyCode)) { + if (e.preventDefault) { + e.preventDefault(); + } + return false; + } + } + }; + } + function makeKeyupHandler(elem) { + return function () { + hidePlaceholder(elem, keydownVal); + + // If the element is now empty we need to show the placeholder + if (elem.value === "") { + elem.blur(); + Utils.moveCaret(elem, 0); + } + }; + } + function makeClickHandler(elem) { + return function () { + if (elem === safeActiveElement() && elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && elem.getAttribute(ATTR_ACTIVE) === "true") { + Utils.moveCaret(elem, 0); + } + }; + } + + // Returns a function that is used as a submit event handler on form elements that have children affected by this polyfill + function makeSubmitHandler(form) { + return function () { + + // Turn off placeholders on all appropriate descendant elements + disablePlaceholders(form); + }; + } + + // Bind event handlers to an element that we need to affect with the polyfill + function newElement(elem) { + + // If the element is part of a form, make sure the placeholder string is not submitted as a value + if (elem.form) { + form = elem.form; + + // If the type of the property is a string then we have a "form" attribute and need to get the real form + if (typeof form === "string") { + form = document.getElementById(form); + } + + // Set a flag on the form so we know it's been handled (forms can contain multiple inputs) + if (!form.getAttribute(ATTR_FORM_HANDLED)) { + Utils.addEventListener(form, "submit", makeSubmitHandler(form)); + form.setAttribute(ATTR_FORM_HANDLED, "true"); + } + } + + // Bind event handlers to the element so we can hide/show the placeholder as appropriate + Utils.addEventListener(elem, "focus", makeFocusHandler(elem)); + Utils.addEventListener(elem, "blur", makeBlurHandler(elem)); + + // If the placeholder should hide on input rather than on focus we need additional event handlers + if (hideOnInput) { + Utils.addEventListener(elem, "keydown", makeKeydownHandler(elem)); + Utils.addEventListener(elem, "keyup", makeKeyupHandler(elem)); + Utils.addEventListener(elem, "click", makeClickHandler(elem)); + } + + // Remember that we've bound event handlers to this element + elem.setAttribute(ATTR_EVENTS_BOUND, "true"); + elem.setAttribute(ATTR_CURRENT_VAL, placeholder); + + // If the element doesn't have a value and is not focussed, set it to the placeholder string + if (hideOnInput || elem !== safeActiveElement()) { + showPlaceholder(elem); + } + } + + Placeholders.nativeSupport = test.placeholder !== void 0; + + if (!Placeholders.nativeSupport) { + + // Get references to all the input and textarea elements currently in the DOM (live NodeList objects to we only need to do this once) + inputs = document.getElementsByTagName("input"); + textareas = document.getElementsByTagName("textarea"); + + // Get any settings declared as data-* attributes on the root element (currently the only options are whether to hide the placeholder on focus or input and whether to auto-update) + hideOnInput = root.getAttribute(ATTR_OPTION_FOCUS) === "false"; + liveUpdates = root.getAttribute(ATTR_OPTION_LIVE) !== "false"; + + // Create style element for placeholder styles (instead of directly setting style properties on elements - allows for better flexibility alongside user-defined styles) + styleElem = document.createElement("style"); + styleElem.type = "text/css"; + + // Create style rules as text node + styleRules = document.createTextNode("." + placeholderClassName + " { color:" + placeholderStyleColor + "; }"); + + // Append style rules to newly created stylesheet + if (styleElem.styleSheet) { + styleElem.styleSheet.cssText = styleRules.nodeValue; + } else { + styleElem.appendChild(styleRules); + } + + // Prepend new style element to the head (before any existing stylesheets, so user-defined rules take precedence) + head.insertBefore(styleElem, head.firstChild); + + // Set up the placeholders + for (i = 0, len = inputs.length + textareas.length; i < len; i++) { + elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length]; + + // Get the value of the placeholder attribute, if any. IE10 emulating IE7 fails with getAttribute, hence the use of the attributes node + placeholder = elem.attributes.placeholder; + if (placeholder) { + + // IE returns an empty object instead of undefined if the attribute is not present + placeholder = placeholder.nodeValue; + + // Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value + if (placeholder && Utils.inArray(validTypes, elem.type)) { + newElement(elem); + } + } + } + + // If enabled, the polyfill will repeatedly check for changed/added elements and apply to those as well + timer = setInterval(function () { + for (i = 0, len = inputs.length + textareas.length; i < len; i++) { + elem = i < inputs.length ? inputs[i] : textareas[i - inputs.length]; + + // Only apply the polyfill if this element is of a type that supports placeholders, and has a placeholder attribute with a non-empty value + placeholder = elem.attributes.placeholder; + if (placeholder) { + placeholder = placeholder.nodeValue; + if (placeholder && Utils.inArray(validTypes, elem.type)) { + + // If the element hasn't had event handlers bound to it then add them + if (!elem.getAttribute(ATTR_EVENTS_BOUND)) { + newElement(elem); + } + + // If the placeholder value has changed or not been initialised yet we need to update the display + if (placeholder !== elem.getAttribute(ATTR_CURRENT_VAL) || (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE))) { + + // Attempt to change the type of password inputs (fails in IE < 9) + if (elem.type === "password" && !elem.getAttribute(ATTR_INPUT_TYPE) && Utils.changeType(elem, "text")) { + elem.setAttribute(ATTR_INPUT_TYPE, "password"); + } + + // If the placeholder value has changed and the placeholder is currently on display we need to change it + if (elem.value === elem.getAttribute(ATTR_CURRENT_VAL)) { + elem.value = placeholder; + } + + // Keep a reference to the current placeholder value in case it changes via another script + elem.setAttribute(ATTR_CURRENT_VAL, placeholder); + } + } + } else if (elem.getAttribute(ATTR_ACTIVE)) { + hidePlaceholder(elem); + elem.removeAttribute(ATTR_CURRENT_VAL); + } + } + + // If live updates are not enabled cancel the timer + if (!liveUpdates) { + clearInterval(timer); + } + }, 100); + } + + Utils.addEventListener(global, "beforeunload", function () { + Placeholders.disable(); + }); + + // Expose public methods + Placeholders.disable = Placeholders.nativeSupport ? noop : disablePlaceholders; + Placeholders.enable = Placeholders.nativeSupport ? noop : enablePlaceholders; + +}(this)); diff --git a/core/templates/layout.base.php b/core/templates/layout.base.php index c519388fa3..b99f603fe0 100644 --- a/core/templates/layout.base.php +++ b/core/templates/layout.base.php @@ -1,10 +1,10 @@ - - - - - - + + + + + + diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php index d38dc24d9c..c4b69a950a 100644 --- a/core/templates/layout.guest.php +++ b/core/templates/layout.guest.php @@ -1,10 +1,10 @@ <!DOCTYPE html> -<!--[if lt IE 7]><html class="ng-csp ie ie6 lte9 lte8 lte7"><![endif]--> -<!--[if IE 7]><html class="ng-csp ie ie7 lte9 lte8 lte7"><![endif]--> -<!--[if IE 8]><html class="ng-csp ie ie8 lte9 lte8"><![endif]--> -<!--[if IE 9]><html class="ng-csp ie ie9 lte9"><![endif]--> -<!--[if gt IE 9]><html class="ng-csp ie"><![endif]--> -<!--[if !IE]><!--><html class="ng-csp"><!--<![endif]--> +<!--[if lt IE 7]><html class="ng-csp ie ie6 lte9 lte8 lte7" data-placeholder-focus="false"><![endif]--> +<!--[if IE 7]><html class="ng-csp ie ie7 lte9 lte8 lte7" data-placeholder-focus="false"><![endif]--> +<!--[if IE 8]><html class="ng-csp ie ie8 lte9 lte8" data-placeholder-focus="false"><![endif]--> +<!--[if IE 9]><html class="ng-csp ie ie9 lte9" data-placeholder-focus="false"><![endif]--> +<!--[if gt IE 9]><html class="ng-csp ie" data-placeholder-focus="false"><![endif]--> +<!--[if !IE]><!--><html class="ng-csp" data-placeholder-focus="false"><!--<![endif]--> <head data-requesttoken="<?php p($_['requesttoken']); ?>"> <title> diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index b0ae8637ac..d551c53521 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -1,10 +1,10 @@ <!DOCTYPE html> -<!--[if lt IE 7]><html class="ng-csp ie ie6 lte9 lte8 lte7"><![endif]--> -<!--[if IE 7]><html class="ng-csp ie ie7 lte9 lte8 lte7"><![endif]--> -<!--[if IE 8]><html class="ng-csp ie ie8 lte9 lte8"><![endif]--> -<!--[if IE 9]><html class="ng-csp ie ie9 lte9"><![endif]--> -<!--[if gt IE 9]><html class="ng-csp ie"><![endif]--> -<!--[if !IE]><!--><html class="ng-csp"><!--<![endif]--> +<!--[if lt IE 7]><html class="ng-csp ie ie6 lte9 lte8 lte7" data-placeholder-focus="false"><![endif]--> +<!--[if IE 7]><html class="ng-csp ie ie7 lte9 lte8 lte7" data-placeholder-focus="false"><![endif]--> +<!--[if IE 8]><html class="ng-csp ie ie8 lte9 lte8" data-placeholder-focus="false"><![endif]--> +<!--[if IE 9]><html class="ng-csp ie ie9 lte9" data-placeholder-focus="false"><![endif]--> +<!--[if gt IE 9]><html class="ng-csp ie" data-placeholder-focus="false"><![endif]--> +<!--[if !IE]><!--><html class="ng-csp" data-placeholder-focus="false"><!--<![endif]--> <head data-user="<?php p($_['user_uid']); ?>" data-requesttoken="<?php p($_['requesttoken']); ?>"> <title> diff --git a/lib/base.php b/lib/base.php index 160d346a01..ea3be3471b 100644 --- a/lib/base.php +++ b/lib/base.php @@ -320,7 +320,7 @@ class OC { OC_Util::addScript("jquery-migrate-1.2.1.min"); OC_Util::addScript("jquery-ui-1.10.0.custom"); OC_Util::addScript("jquery-showpassword"); - OC_Util::addScript("jquery.placeholder"); + OC_Util::addScript("placeholders"); OC_Util::addScript("jquery-tipsy"); OC_Util::addScript("compatibility"); OC_Util::addScript("underscore");