New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

angular-shims-placeholder

Package Overview
Dependencies
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

angular-shims-placeholder - npm Package Compare versions

Comparing version 0.3.1 to 0.3.2

2

bower.json
{
"name": "angular-shims-placeholder",
"description": "Angular directive to emulate the `placeholder` attribute on text and password input fields for old browsers, such as IE9, IE8, and below",
"version": "0.3.1",
"version": "0.3.2",
"homepage": "https://github.com/jrief/angular-shims-placeholder",

@@ -6,0 +6,0 @@ "authors": [{

@@ -1,2 +0,2 @@

/*! angular-shims-placeholder - v0.3.1 - 2014-11-03
/*! angular-shims-placeholder - v0.3.2 - 2014-11-19
* https://github.com/jrief/angular-shims-placeholder

@@ -9,3 +9,3 @@ * Copyright (c) 2014 Jacob Rief; Licensed MIT */

function ($document) {
this.hasPlaceholder = function () {
this.emptyClassName = 'empty', this.hasPlaceholder = function () {
var test = $document[0].createElement('input');

@@ -17,12 +17,14 @@ return test.placeholder !== void 0;

'$timeout',
'$document',
'placeholderSniffer',
function ($timeout, placeholderSniffer) {
function ($timeout, $document, placeholderSniffer) {
if (placeholderSniffer.hasPlaceholder())
return {};
var documentListenersApplied = false;
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
priority: 110,
link: function (scope, elem, attrs, ngModel) {
var orig_val = getValue(), domElem = elem[0], elemType = domElem.nodeName.toLowerCase(), isInput = elemType === 'input' || elemType === 'textarea', is_pwd = attrs.type === 'password', text = attrs.placeholder, emptyClassName = 'empty', clone;
var orig_val = getValue(), domElem = elem[0], elemType = domElem.nodeName.toLowerCase(), isInput = elemType === 'input' || elemType === 'textarea', is_pwd = attrs.type === 'password', text = attrs.placeholder, emptyClassName = placeholderSniffer.emptyClassName, hiddenClassName = 'ng-hide', clone;
if (!text || !isInput) {

@@ -54,2 +56,11 @@ return;

}
if (!documentListenersApplied) {
$document.on('selectstart', function (e) {
var elmn = angular.element(e.target);
if (elmn.hasClass(emptyClassName) && elmn.prop('disabled')) {
e.preventDefault();
}
});
documentListenersApplied = true;
}
function updateValue(e) {

@@ -60,8 +71,11 @@ var val = elem.val();

}
conditionalDefer(function () {
setValue(val);
});
}
function conditionalDefer(callback) {
if (document.documentMode <= 11) {
$timeout(function () {
setValue(val);
}, 0);
$timeout(callback, 0);
} else {
setValue(val);
callback();
}

@@ -72,5 +86,3 @@ }

elem.addClass(emptyClassName);
if (is_pwd) {
showPasswordPlaceholder();
} else {
if (!is_pwd) {
elem.val(text);

@@ -80,7 +92,7 @@ }

elem.removeClass(emptyClassName);
if (is_pwd) {
hidePasswordPlaceholder();
}
elem.val(val);
}
if (is_pwd) {
updatePasswordPlaceholder();
}
}

@@ -100,19 +112,48 @@ function getValue() {

}
function setAttrUnselectable(elmn, enable) {
if (enable) {
elmn.attr('unselectable', 'on');
} else {
elmn.removeAttr('unselectable');
}
}
function setupPasswordPlaceholder() {
clone = angular.element('<input type="text" value="' + text + '"/>');
stylePasswordPlaceholder();
clone.addClass(emptyClassName).addClass('ng-hide').bind('focus', hidePasswordPlaceholderAndFocus);
clone.addClass(emptyClassName).addClass(hiddenClassName).bind('focus', hidePasswordPlaceholderAndFocus);
domElem.parentNode.insertBefore(clone[0], domElem);
var watchAttrs = [
attrs.ngDisabled,
attrs.ngReadonly,
attrs.ngRequired,
attrs.ngShow,
attrs.ngHide
];
for (var i = 0; i < watchAttrs.length; i++) {
if (watchAttrs[i]) {
scope.$watch(watchAttrs[i], updatePasswordPlaceholder);
}
}
}
function updatePasswordPlaceholder() {
stylePasswordPlaceholder();
if (isNgHidden()) {
clone.addClass(hiddenClassName);
} else if (elem.hasClass(emptyClassName) && domElem !== document.activeElement) {
showPasswordPlaceholder();
} else {
hidePasswordPlaceholder();
}
}
function stylePasswordPlaceholder() {
clone.val(text).attr('class', elem.attr('class') || '').attr('style', elem.attr('style') || '');
clone.val(text).attr('class', elem.attr('class') || '').attr('style', elem.attr('style') || '').prop('disabled', elem.prop('disabled')).prop('readOnly', elem.prop('readOnly')).prop('required', elem.prop('required'));
setAttrUnselectable(clone, elem.attr('unselectable') === 'on');
}
function showPasswordPlaceholder() {
stylePasswordPlaceholder();
elem.addClass('ng-hide');
clone.removeClass('ng-hide');
elem.addClass(hiddenClassName);
clone.removeClass(hiddenClassName);
}
function hidePasswordPlaceholder() {
clone.addClass('ng-hide');
elem.removeClass('ng-hide');
clone.addClass(hiddenClassName);
elem.removeClass(hiddenClassName);
}

@@ -123,2 +164,10 @@ function hidePasswordPlaceholderAndFocus() {

}
function isNgHidden() {
var hasNgShow = typeof attrs.ngShow !== 'undefined', hasNgHide = typeof attrs.ngHide !== 'undefined';
if (hasNgShow || hasNgHide) {
return hasNgShow && !scope.$eval(attrs.ngShow) || hasNgHide && scope.$eval(attrs.ngHide);
} else {
return false;
}
}
}

@@ -125,0 +174,0 @@ };

@@ -1,4 +0,4 @@

/*! angular-shims-placeholder - v0.3.1 - 2014-11-03
/*! angular-shims-placeholder - v0.3.2 - 2014-11-19
* https://github.com/jrief/angular-shims-placeholder
* Copyright (c) 2014 Jacob Rief; Licensed MIT */
!function(a,b){"use strict";a.module("ng.shims.placeholder",[]).service("placeholderSniffer",["$document",function(a){this.hasPlaceholder=function(){var b=a[0].createElement("input");return void 0!==b.placeholder}}]).directive("placeholder",["$timeout","placeholderSniffer",function(c,d){return d.hasPlaceholder()?{}:{restrict:"A",require:"?ngModel",priority:1,link:function(d,e,f,g){function h(){var a=e.val();e.hasClass(x)&&a===w||(b.documentMode<=11?c(function(){i(a)},0):i(a))}function i(a){a||s===b.activeElement?(e.removeClass(x),v&&o(),e.val(a)):(e.addClass(x),v?n():e.val(w))}function j(){return g?d.$eval(f.ngModel)||"":k()||""}function k(){var a=e.val();return a===f.placeholder&&(a=""),a}function l(){q=a.element('<input type="text" value="'+w+'"/>'),m(),q.addClass(x).addClass("ng-hide").bind("focus",p),s.parentNode.insertBefore(q[0],s)}function m(){q.val(w).attr("class",e.attr("class")||"").attr("style",e.attr("style")||"")}function n(){m(),e.addClass("ng-hide"),q.removeClass("ng-hide")}function o(){q.addClass("ng-hide"),e.removeClass("ng-hide")}function p(){o(),s.focus()}var q,r=j(),s=e[0],t=s.nodeName.toLowerCase(),u="input"===t||"textarea"===t,v="password"===f.type,w=f.placeholder,x="empty";w&&u&&(v&&l(),i(r),e.bind("focus",function(){e.hasClass(x)&&(e.val(""),e.removeClass(x),s.select())}),e.bind("blur",h),g||e.bind("change",h),g&&(g.$render=function(){i(g.$viewValue),s!==b.activeElement||e.val()||s.select()}))}}}])}(window.angular,window.document);
!function(a,b){"use strict";a.module("ng.shims.placeholder",[]).service("placeholderSniffer",["$document",function(a){this.emptyClassName="empty",this.hasPlaceholder=function(){var b=a[0].createElement("input");return void 0!==b.placeholder}}]).directive("placeholder",["$timeout","$document","placeholderSniffer",function(c,d,e){if(e.hasPlaceholder())return{};var f=!1;return{restrict:"A",require:"?ngModel",priority:110,link:function(g,h,i,j){function k(){var a=h.val();h.hasClass(E)&&a===D||l(function(){m(a)})}function l(a){b.documentMode<=11?c(a,0):a()}function m(a){a||z===b.activeElement?(h.removeClass(E),h.val(a)):(h.addClass(E),C||h.val(D)),C&&r()}function n(){return j?g.$eval(i.ngModel)||"":o()||""}function o(){var a=h.val();return a===i.placeholder&&(a=""),a}function p(a,b){b?a.attr("unselectable","on"):a.removeAttr("unselectable")}function q(){x=a.element('<input type="text" value="'+D+'"/>'),s(),x.addClass(E).addClass(F).bind("focus",v),z.parentNode.insertBefore(x[0],z);for(var b=[i.ngDisabled,i.ngReadonly,i.ngRequired,i.ngShow,i.ngHide],c=0;c<b.length;c++)b[c]&&g.$watch(b[c],r)}function r(){s(),w()?x.addClass(F):h.hasClass(E)&&z!==b.activeElement?t():u()}function s(){x.val(D).attr("class",h.attr("class")||"").attr("style",h.attr("style")||"").prop("disabled",h.prop("disabled")).prop("readOnly",h.prop("readOnly")).prop("required",h.prop("required")),p(x,"on"===h.attr("unselectable"))}function t(){h.addClass(F),x.removeClass(F)}function u(){x.addClass(F),h.removeClass(F)}function v(){u(),z.focus()}function w(){var a="undefined"!=typeof i.ngShow,b="undefined"!=typeof i.ngHide;return a||b?a&&!g.$eval(i.ngShow)||b&&g.$eval(i.ngHide):!1}var x,y=n(),z=h[0],A=z.nodeName.toLowerCase(),B="input"===A||"textarea"===A,C="password"===i.type,D=i.placeholder,E=e.emptyClassName,F="ng-hide";D&&B&&(C&&q(),m(y),h.bind("focus",function(){h.hasClass(E)&&(h.val(""),h.removeClass(E),z.select())}),h.bind("blur",k),j||h.bind("change",k),j&&(j.$render=function(){m(j.$viewValue),z!==b.activeElement||h.val()||z.select()}),f||(d.on("selectstart",function(b){var c=a.element(b.target);c.hasClass(E)&&c.prop("disabled")&&b.preventDefault()}),f=!0))}}}])}(window.angular,window.document);

@@ -17,2 +17,3 @@ /*

.service('placeholderSniffer', function($document){
this.emptyClassName = 'empty',
this.hasPlaceholder = function() {

@@ -24,5 +25,7 @@ // test for native placeholder support

})
.directive('placeholder', function($timeout, placeholderSniffer) {
.directive('placeholder', function($timeout, $document, placeholderSniffer) {
if (placeholderSniffer.hasPlaceholder()) return {};
var documentListenersApplied = false;
// No native support for attribute placeholder

@@ -32,3 +35,3 @@ return {

require: '?ngModel',
priority: 1,
priority: 110, // run after ngModel (0) and BOOLEAN_ATTR (100) directives
link: function(scope, elem, attrs, ngModel) {

@@ -41,3 +44,4 @@ var orig_val = getValue(),

text = attrs.placeholder,
emptyClassName = 'empty',
emptyClassName = placeholderSniffer.emptyClassName,
hiddenClassName = 'ng-hide',
clone;

@@ -65,2 +69,3 @@

// handler for model-less inputs to interact with non-angular code
// TODO: vs `$watch(function(){return elem.val()})`
if (!ngModel) {

@@ -86,2 +91,15 @@ elem.bind('change', updateValue);

if (!documentListenersApplied) {
// cancel selection of placeholder text on disabled elements
// disabled elements do not emit selectstart events in IE8/IE9,
// so bind to $document and catch the event as it bubbles
$document.on('selectstart', function (e) {
var elmn = angular.element(e.target);
if (elmn.hasClass(emptyClassName) && elmn.prop('disabled')) {
e.preventDefault();
}
});
documentListenersApplied = true;
}
function updateValue(e) {

@@ -93,16 +111,18 @@ var val = elem.val();

conditionalDefer(function(){ setValue(val); });
}
function conditionalDefer(callback) {
// IE8/9: ngModel uses a keydown handler with deferrered
// execution to check for changes to the input. this $timeout
// prevents updateValue from firing before the keydown handler,
// prevents callback from firing before the keydown handler,
// which is an issue when tabbing out of an input.
// the conditional tests IE version, matches $sniffer.
//
// TODO: remove timeout path when tab key behavior is fixed in
// TODO: remove this function when tab key behavior is fixed in
// angular core
if (document.documentMode <= 11) {
$timeout(function(){
setValue(val);
},0);
$timeout(callback, 0);
} else {
setValue(val);
callback();
}

@@ -115,5 +135,3 @@ }

elem.addClass(emptyClassName);
if (is_pwd) {
showPasswordPlaceholder();
} else {
if (!is_pwd) {
elem.val(text);

@@ -124,7 +142,7 @@ }

elem.removeClass(emptyClassName);
if (is_pwd) {
hidePasswordPlaceholder();
}
elem.val(val);
}
if (is_pwd) {
updatePasswordPlaceholder();
}
}

@@ -134,2 +152,4 @@

if (ngModel) {
// use eval because $viewValue isn't ready during init
// TODO: this might not to work during unit tests, investigate
return scope.$eval(attrs.ngModel) || '';

@@ -143,2 +163,4 @@ }

// http://stackoverflow.com/q/11208417/490592
// I believe IE is persisting the field value across refreshes
// TODO: vs `elem.attr('value')`
function getDomValue() {

@@ -152,2 +174,10 @@ var val = elem.val();

function setAttrUnselectable(elmn, enable) {
if (enable) {
elmn.attr('unselectable', 'on');
} else {
elmn.removeAttr('unselectable');
}
}
// IE8: password inputs cannot display text, and inputs cannot

@@ -159,22 +189,56 @@ // change type, so create a new element to display placeholder

clone.addClass(emptyClassName)
.addClass('ng-hide')
.addClass(hiddenClassName)
.bind('focus', hidePasswordPlaceholderAndFocus);
domElem.parentNode.insertBefore(clone[0], domElem);
// keep password placeholder in sync with original element.
// update element after $watches
var watchAttrs = [
attrs.ngDisabled,
attrs.ngReadonly,
attrs.ngRequired,
attrs.ngShow,
attrs.ngHide
];
for (var i = 0; i < watchAttrs.length; i++) {
if (watchAttrs[i]) {
scope.$watch(watchAttrs[i], updatePasswordPlaceholder);
}
}
}
function updatePasswordPlaceholder() {
stylePasswordPlaceholder();
if (isNgHidden()) {
// force hide the placeholder when element is hidden by
// ngShow/ngHide. we cannot rely on stylePasswordPlaceholder
// above to copy the ng-hide class, because the ngShow/ngHide
// $watch functions apply the ng-hide class with $animate,
// so the class is not applied when our $watch executes
clone.addClass(hiddenClassName);
} else if (elem.hasClass(emptyClassName) && domElem !== document.activeElement) {
showPasswordPlaceholder();
} else {
hidePasswordPlaceholder();
}
}
function stylePasswordPlaceholder() {
clone.val(text)
.attr('class', elem.attr('class') || '')
.attr('style', elem.attr('style') || '');
.attr('style', elem.attr('style') || '')
.prop('disabled', elem.prop('disabled'))
.prop('readOnly', elem.prop('readOnly'))
.prop('required', elem.prop('required'));
setAttrUnselectable(clone, elem.attr('unselectable') === 'on');
}
function showPasswordPlaceholder() {
stylePasswordPlaceholder();
elem.addClass('ng-hide');
clone.removeClass('ng-hide');
elem.addClass(hiddenClassName);
clone.removeClass(hiddenClassName);
}
function hidePasswordPlaceholder() {
clone.addClass('ng-hide');
elem.removeClass('ng-hide');
clone.addClass(hiddenClassName);
elem.removeClass(hiddenClassName);
}

@@ -187,2 +251,13 @@

function isNgHidden() {
var hasNgShow = typeof attrs.ngShow !== 'undefined',
hasNgHide = typeof attrs.ngHide !== 'undefined';
if (hasNgShow || hasNgHide) {
return (hasNgShow && !scope.$eval(attrs.ngShow)) ||
(hasNgHide && scope.$eval(attrs.ngHide));
} else {
return false;
}
}
}

@@ -189,0 +264,0 @@ };

{
"name": "angular-shims-placeholder",
"description": "Angular directive to emulate the `placeholder` attribute on text and password input fields for old browsers, such as IE9, IE8, and below",
"version": "0.3.1",
"version": "0.3.2",
"homepage": "https://github.com/jrief/angular-shims-placeholder",

@@ -34,5 +34,5 @@ "author": {

"grunt": ">= 0.4.0",
"grunt-bump": "0.0.16",
"grunt-bump": "latest",
"grunt-contrib-concat": "latest",
"grunt-contrib-connect": "^0.8.0",
"grunt-contrib-connect": "latest",
"grunt-contrib-jshint": "latest",

@@ -42,3 +42,3 @@ "grunt-contrib-uglify": "latest",

"grunt-ngmin": "0.0.2",
"grunt-open": "^0.2.3",
"grunt-open": "latest",
"karma": "0.8.5"

@@ -45,0 +45,0 @@ },

angular-shims-placeholder
=========================
[![Bower version](https://badge.fury.io/bo/angular-shims-placeholder.svg)](http://badge.fury.io/bo/angular-shims-placeholder)
[![npm version](https://badge.fury.io/js/angular-shims-placeholder.svg)](http://badge.fury.io/js/angular-shims-placeholder)
[![Build Status](https://travis-ci.org/jrief/angular-shims-placeholder.svg?branch=master)](https://travis-ci.org/jrief/angular-shims-placeholder)
[![devDependency Status](https://david-dm.org/jrief/angular-shims-placeholder/dev-status.svg)](https://david-dm.org/jrief/angular-shims-placeholder#info=devDependencies)

@@ -14,9 +18,7 @@ Angular directive to emulate the `placeholder` attribute on text and password input fields for

Demo
----
[View Demo](http://jrief.github.io/angular-shims-placeholder)
Build status
------------
[![Build Status](https://travis-ci.org/jrief/angular-shims-placeholder.png?branch=master)](https://travis-ci.org/jrief/angular-shims-placeholder)
Usage

@@ -50,2 +52,26 @@ -----

Compatibility
-------------
This directive is compatible with ngModel, ngDisabled, ngReadonly, ngRequired, ngShow, and ngHide.
If you modify a shimmed input from outside of Angular, use the 'change' event to update the placeholder display. e.g. `elem.triggerHandler('change')`
Known Issues
------------
* Ignores text input from drag and drop
* Does not support modern-style placeholders that persist until text is entered
* IE8/9: Disabled textareas show the text insertion cursor on hover. This is due to an IE bug.
* IE8/9: Clearing a filled input while its text is selected can cause the resulting placeholder text to appear selected
* No way for an individual input to opt out
* Not tested with ngAnimate
* Not tested with ngSubmit
* Not tested with ngClass
* Not tested with dynamic placeholders e.g. placeholder="{{val}}"
Authors
-------
Written by [Jacob Rief](https://github.com/jrief).
Maintained by [Chad von Nau](https://github.com/cvn).
License

@@ -52,0 +78,0 @@ -------

@@ -284,2 +284,26 @@ 'use strict';

});
describe('on a empty password input with unselectable=on attribute', function() {
var pwd_field, pwd_clone;
beforeEach(function() {
pwd_field = angular.element('<input type="password" name="userpwd" placeholder="Password" unselectable="on" value="" />');
$compile(pwd_field)(scope);
pwd_clone = angular.element(pwd_field[0].previousElementSibling);
});
describe('the placeholder element', function() {
it('should be unselectable', function() {
expect(pwd_clone.val()).toBe('Password');
expect(pwd_clone.attr('unselectable')).toBe('on');
});
it('should become selectable when disabled attribute is removed', function() {
pwd_field.removeAttr('unselectable');
pwd_field.triggerHandler('change');
expect(pwd_clone.val()).toBe('Password');
expect(pwd_clone.attr('unselectable')).toBeUndefined();
});
});
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc