├── .gitignore
├── .travis.yml
├── LICENSE.md
├── README.md
├── dist
├── css
│ ├── x-ray.css
│ └── x-ray.min.css
└── js
│ ├── x-ray.js
│ └── x-ray.min.js
├── docs
├── assets
│ └── css
│ │ └── custom.css
├── dist
│ ├── css
│ │ ├── x-ray.css
│ │ └── x-ray.min.css
│ └── js
│ │ ├── x-ray.js
│ │ └── x-ray.min.js
└── index.html
├── gulpfile.js
├── package.json
└── src
├── docs
├── _templates
│ ├── _footer.html
│ └── _header.html
├── assets
│ └── css
│ │ └── custom.css
└── index.md
├── js
└── x-ray.js
└── sass
├── components
└── _x-ray.scss
└── x-ray.scss
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node
2 | node_modules
3 | test/results
4 | test/coverage
5 |
6 | ## OS X
7 | .DS_Store
8 | ._*
9 | .Spotlight-V100
10 | .Trashes
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | before_script:
5 | - npm install -g gulp
6 | script: gulp
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # The MIT License (MIT)
2 |
3 | Copyright (c) Go Make Things, LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # X-Ray [](https://travis-ci.org/cferdinandi/x-ray)
2 | A script that lets users toggle password visibility in forms.
3 |
4 | [Download X-Ray](https://github.com/cferdinandi/x-ray/archive/master.zip) / [View the demo](http://cferdinandi.github.io/x-ray/)
5 |
6 |
7 |
8 |
9 | ### Want to learn how to write your own vanilla JS plugins? Check out ["The Vanilla JS Guidebook"](https://gomakethings.com/vanilla-js-guidebook/) and level-up as a web developer. 🚀
10 |
11 |
12 |
13 |
14 |
15 | ## Getting Started
16 |
17 | Compiled and production-ready code can be found in the `dist` directory. The `src` directory contains development code.
18 |
19 | ### 1. Include X-Ray on your site.
20 |
21 | ```html
22 |
23 |
24 | ```
25 |
26 | ### 2. Add the markup to your HTML.
27 |
28 | ```html
29 |
41 | ```
42 |
43 | Turn any link or button into a password visibility toggle by adding the `.x-ray` class and `[data-x-ray]` attribute. The value of the `[data-x-ray]` attribute should match the selector for the target password field. If you would like passwords to be visible by default, set the optional `[data-default]` attribute to `show`.
44 |
45 | Use `` elements with the `.x-ray-show` class and `[data-x-ray-show]` data attribute or `.x-ray-hide` class and `[data-x-ray-hide]` data attribute to change the toggle element based on whether or not the password is visible.
46 |
47 | #### Using Checkboxes
48 |
49 | If you'd prefer, you can use a checkbox instead of a button to toggle password visibility.
50 |
51 | ```html
52 |
68 | ```
69 |
70 | #### Toggling Multiple Password Fields
71 |
72 | You can toggle multiple password fields with one button or checkbox by using a class selector instead of an ID.
73 |
74 | ```html
75 |
91 | ```
92 |
93 | ### 3. Initialize X-Ray.
94 |
95 | ```html
96 |
99 | ```
100 |
101 | In the footer of your page, after the content, initialize X-Ray. And that's it, you're done. Nice work!
102 |
103 |
104 |
105 | ## Installing with Package Managers
106 |
107 | You can install X-Ray with your favorite package manager.
108 |
109 | * **NPM:** `npm install cferdinandi/x-ray`
110 | * **Bower:** `bower install https://github.com/cferdinandi/x-ray.git`
111 | * **Component:** `component install cferdinandi/x-ray`
112 |
113 |
114 |
115 | ## Working with the Source Files
116 |
117 | If you would prefer, you can work with the development code in the `src` directory using the included [Gulp build system](http://gulpjs.com/). This compiles, lints, and minifies code.
118 |
119 | ### Dependencies
120 | Make sure these are installed first.
121 |
122 | * [Node.js](http://nodejs.org)
123 | * [Gulp](http://gulpjs.com) `sudo npm install -g gulp`
124 |
125 | ### Quick Start
126 |
127 | 1. In bash/terminal/command line, `cd` into your project directory.
128 | 2. Run `npm install` to install required files.
129 | 3. When it's done installing, run one of the task runners to get going:
130 | * `gulp` manually compiles files.
131 | * `gulp watch` automatically compiles files and applies changes using [LiveReload](http://livereload.com/).
132 |
133 |
134 |
135 | ## Options and Settings
136 |
137 | X-Ray includes smart defaults and works right out of the box. But if you want to customize things, it also has a robust API that provides multiple ways for you to adjust the default options and settings.
138 |
139 | ### Global Settings
140 |
141 | You can pass options and callbacks into X-Ray through the `init()` function:
142 |
143 | ```javascript
144 | xray.init({
145 | selector: '[data-x-ray]', // Selector for the password toggle (must be a valid CSS selector)
146 | selectorShow: '[data-x-ray-show]', // Selector for the "show password" text (must be a valid CSS selector)
147 | selectorHide: '[data-x-ray-hide]', // Selector for the "hide password" text (must be a valid CSS selector)
148 | toggleActiveClass: 'active', // Class added to active password toggle button
149 | initClass: 'js-x-ray', // Class added to element when initiated
150 | callback: function ( toggle, pwID ) {} // Function that's run after password visibility is toggled
151 | });
152 | ```
153 |
154 | ### Use X-Ray events in your own scripts
155 |
156 | You can also call X-Ray's toggle password event in your own scripts.
157 |
158 | #### runToggle()
159 | Toggle password visibility on or off.
160 |
161 | ```javascript
162 | xray.runToggle(
163 | toggle, // Node that toggles the password visibility. ex. document.querySelector('[data-x-ray="#pw"]')
164 | pwID, // The ID or class of the password area(s) to show. ex. '#pw'
165 | options, // Classes and callbacks. Same options as those passed into the init() function.
166 | event // Optional, if a DOM event was triggered.
167 | );
168 | ```
169 |
170 | **Example**
171 |
172 | ```javascript
173 | var toggle = document.querySelector('[data-x-ray="#pw"]');
174 | xray.runToggle( toggle, '#pw' );
175 | ```
176 |
177 | #### destroy()
178 | Destroy the current `xray.init()`. This is called automatically during the init function to remove any existing initializations.
179 |
180 | ```javascript
181 | xray.destroy();
182 | ```
183 |
184 |
185 |
186 | ## Browser Compatibility
187 |
188 | X-Ray works in all modern browsers, and IE 10 and above. You can extend browser support back to IE 9 with the [classList.js polyfill](https://github.com/eligrey/classList.js/).
189 |
190 | X-Ray is built with modern JavaScript APIs, and uses progressive enhancement. If the JavaScript file fails to load, or if your site is viewed on older and less capable browsers, passwords will be masked by default.
191 |
192 |
193 |
194 | ## How to Contribute
195 |
196 | In lieu of a formal style guide, take care to maintain the existing coding style. Please apply fixes to both the development and production code. Don't forget to update the version number, and when applicable, the documentation.
197 |
198 |
199 |
200 | ## License
201 |
202 | The code is available under the [MIT License](LICENSE.md).
--------------------------------------------------------------------------------
/dist/css/x-ray.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * x-ray v9.2.0: Toggle password visibility
3 | * (c) 2016 Chris Ferdinandi
4 | * MIT License
5 | * http://github.com/cferdinandi/x-ray
6 | */
7 |
8 | /* Hide show/hide labels by default */
9 | /* line 2, /Users/cferdinandi/Sites/x-ray/src/sass/components/_x-ray.scss */
10 | .x-ray,
11 | .x-ray-show,
12 | .x-ray-hide {
13 | display: none;
14 | visibility: hidden;
15 | }
16 |
17 | /* Display show/hide toggle when modern JS API's supported.
18 | * Display show/hide labels when active. */
19 | /* line 11, /Users/cferdinandi/Sites/x-ray/src/sass/components/_x-ray.scss */
20 | .js-x-ray .x-ray,
21 | .x-ray-show.active,
22 | .x-ray-hide.active {
23 | display: inline;
24 | visibility: visible;
25 | }
26 |
--------------------------------------------------------------------------------
/dist/css/x-ray.min.css:
--------------------------------------------------------------------------------
1 | /*! x-ray v9.2.0 | (c) 2016 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/x-ray */
2 | .x-ray,.x-ray-hide,.x-ray-show{display:none;visibility:hidden}.js-x-ray .x-ray,.x-ray-hide.active,.x-ray-show.active{display:inline;visibility:visible}
--------------------------------------------------------------------------------
/dist/js/x-ray.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * x-ray v9.2.0: Toggle password visibility
3 | * (c) 2016 Chris Ferdinandi
4 | * MIT License
5 | * http://github.com/cferdinandi/x-ray
6 | */
7 |
8 | (function (root, factory) {
9 | if ( typeof define === 'function' && define.amd ) {
10 | define([], factory(root));
11 | } else if ( typeof exports === 'object' ) {
12 | module.exports = factory(root);
13 | } else {
14 | root.xray = factory(root);
15 | }
16 | })(typeof global !== 'undefined' ? global : this.window || this.global, function (root) {
17 |
18 | 'use strict';
19 |
20 | //
21 | // Variables
22 | //
23 |
24 | var xray = {}; // Object for public APIs
25 | var supports = 'querySelector' in document && 'addEventListener' in root && 'classList' in document.createElement('_'); // Feature test
26 | var settings, toggles;
27 |
28 | // Default settings
29 | var defaults = {
30 | selector: '[data-x-ray]',
31 | selectorShow: '[data-x-ray-show]',
32 | selectorHide: '[data-x-ray-hide]',
33 | toggleActiveClass: 'active',
34 | initClass: 'js-x-ray',
35 | callback: function () {}
36 | };
37 |
38 |
39 | //
40 | // Methods
41 | //
42 |
43 | /**
44 | * A simple forEach() implementation for Arrays, Objects and NodeLists.
45 | * @private
46 | * @author Todd Motto
47 | * @link https://github.com/toddmotto/foreach
48 | * @param {Array|Object|NodeList} collection Collection of items to iterate
49 | * @param {Function} callback Callback function for each iteration
50 | * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
51 | */
52 | var forEach = function ( collection, callback, scope ) {
53 | if ( Object.prototype.toString.call( collection ) === '[object Object]' ) {
54 | for ( var prop in collection ) {
55 | if ( Object.prototype.hasOwnProperty.call( collection, prop ) ) {
56 | callback.call( scope, collection[prop], prop, collection );
57 | }
58 | }
59 | } else {
60 | for ( var i = 0, len = collection.length; i < len; i++ ) {
61 | callback.call( scope, collection[i], i, collection );
62 | }
63 | }
64 | };
65 |
66 | /**
67 | * Merge two or more objects. Returns a new object.
68 | * @private
69 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional]
70 | * @param {Object} objects The objects to merge together
71 | * @returns {Object} Merged values of defaults and options
72 | */
73 | var extend = function () {
74 |
75 | // Variables
76 | var extended = {};
77 | var deep = false;
78 | var i = 0;
79 | var length = arguments.length;
80 |
81 | // Check if a deep merge
82 | if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
83 | deep = arguments[0];
84 | i++;
85 | }
86 |
87 | // Merge the object into the extended object
88 | var merge = function (obj) {
89 | for ( var prop in obj ) {
90 | if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
91 | // If deep merge and property is an object, merge properties
92 | if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {
93 | extended[prop] = extend( true, extended[prop], obj[prop] );
94 | } else {
95 | extended[prop] = obj[prop];
96 | }
97 | }
98 | }
99 | };
100 |
101 | // Loop through each object and conduct a merge
102 | for ( ; i < length; i++ ) {
103 | var obj = arguments[i];
104 | merge(obj);
105 | }
106 |
107 | return extended;
108 |
109 | };
110 |
111 | /**
112 | * Get the closest matching element up the DOM tree.
113 | * @private
114 | * @param {Element} elem Starting element
115 | * @param {String} selector Selector to match against
116 | * @return {Boolean|Element} Returns null if not match found
117 | */
118 | var getClosest = function ( elem, selector ) {
119 |
120 | // Element.matches() polyfill
121 | if (!Element.prototype.matches) {
122 | Element.prototype.matches =
123 | Element.prototype.matchesSelector ||
124 | Element.prototype.mozMatchesSelector ||
125 | Element.prototype.msMatchesSelector ||
126 | Element.prototype.oMatchesSelector ||
127 | Element.prototype.webkitMatchesSelector ||
128 | function(s) {
129 | var matches = (this.document || this.ownerDocument).querySelectorAll(s),
130 | i = matches.length;
131 | while (--i >= 0 && matches.item(i) !== this) {}
132 | return i > -1;
133 | };
134 | }
135 |
136 | // Get closest match
137 | for ( ; elem && elem !== document; elem = elem.parentNode ) {
138 | if ( elem.matches( selector ) ) return elem;
139 | }
140 |
141 | return null;
142 |
143 | };
144 |
145 | /**
146 | * Toggle password visibility
147 | * @private
148 | * @param {NodeList} pws Password fields to toggle
149 | */
150 | var togglePW = function ( pws ) {
151 | forEach(pws, function (pw) {
152 | var pwType = pw.type.toLowerCase();
153 | if ( pwType === 'password' ) {
154 | pw.type = 'text';
155 | } else if ( pwType === 'text' ) {
156 | pw.type = 'password';
157 | }
158 | });
159 | };
160 |
161 | /**
162 | * Load default visibility
163 | * @private
164 | * @param {Element} toggle The element that toggles password visibility
165 | * @param {String} visibility Should the password be visible or hidden by default?
166 | * @param {String} pwSelector ID of the password field
167 | * @param {Object} settings
168 | */
169 | var loadDefaultVisibility = function ( toggle, visibility, pwSelector, settings ) {
170 | var showText = toggle.querySelector( settings.selectorShow );
171 | var hideText = toggle.querySelector( settings.selectorHide );
172 | var pws = document.querySelectorAll(pwSelector);
173 | if ( visibility === 'show' ) {
174 | togglePW(pws);
175 | if ( hideText ) {
176 | hideText.classList.add( settings.toggleActiveClass );
177 | }
178 | } else {
179 | if ( showText ) {
180 | showText.classList.add( settings.toggleActiveClass );
181 | }
182 | }
183 | };
184 |
185 | /**
186 | * Update toggle text
187 | * @private
188 | * @param {Element} toggle The element that toggles password visibility
189 | * @param {Object} settings
190 | */
191 | var updateToggleText = function ( toggle, settings ) {
192 | var showText = toggle.querySelector('.x-ray-show');
193 | var hideText = toggle.querySelector('.x-ray-hide');
194 | if ( hideText ) {
195 | hideText.classList.toggle( settings.toggleActiveClass );
196 | }
197 | if ( showText ) {
198 | showText.classList.toggle( settings.toggleActiveClass );
199 | }
200 | };
201 |
202 | /**
203 | * Show or hide password visibility
204 | * @public
205 | * @param {Element} toggle The element that toggles password visibility
206 | * @param {String} pwSelector The selector for the password fields
207 | * @param {Object} options
208 | * @param {Event} event
209 | */
210 | xray.runToggle = function ( toggle, pwSelector, options, event ) {
211 |
212 | // Selectors and variables
213 | var settings = extend( settings || defaults, options || {} ); // Merge user options with defaults
214 | var pws = document.querySelectorAll( pwSelector );
215 |
216 | togglePW( pws ); // Show/Hide password
217 | updateToggleText( toggle, settings ); // Change the toggle text
218 |
219 | settings.callback( toggle, pwSelector ); // Run callbacks after password visibility toggle
220 |
221 | };
222 |
223 | /**
224 | * Handle toggle click events
225 | * @private
226 | */
227 | var eventHandler = function (event) {
228 | var toggle = getClosest( event.target, settings.selector );
229 | if ( toggle ) {
230 | if ( toggle.tagName.toLowerCase() === 'a' || toggle.tagName.toLowerCase() === 'button' ) {
231 | event.preventDefault();
232 | }
233 | xray.runToggle( toggle, toggle.getAttribute('data-x-ray'), settings );
234 | }
235 | };
236 |
237 | /**
238 | * Destroy the current initialization.
239 | * @public
240 | */
241 | xray.destroy = function () {
242 | if ( !settings ) return;
243 | document.documentElement.classList.remove( settings.initClass );
244 | document.removeEventListener('click', eventHandler, false);
245 | if ( toggles ) {
246 | forEach( toggles, function ( toggle ) {
247 |
248 | // Get elements
249 | var pws = document.querySelectorAll( toggle.getAttribute('data-x-ray') );
250 | var showText = toggle.querySelector( settings.selectorShow );
251 | var hideText = toggle.querySelector( settings.selectorHide );
252 |
253 | // Reset to default password state
254 | forEach( pws, function ( pw ) {
255 | pw.type = 'password';
256 | });
257 | showText.classList.remove(settings.toggleActiveClass);
258 | hideText.classList.remove(settings.toggleActiveClass);
259 |
260 | });
261 | }
262 | settings = null;
263 | toggles = null;
264 | };
265 |
266 | /**
267 | * Initialize X-Ray
268 | * @public
269 | * @param {Object} options User settings
270 | */
271 | xray.init = function ( options ) {
272 |
273 | // feature test
274 | if ( !supports ) return;
275 |
276 | // Destroy any existing initializations
277 | xray.destroy();
278 |
279 | // Selectors and variables
280 | settings = extend( defaults, options || {} ); // Merge user options with defaults
281 | toggles = document.querySelectorAll( settings.selector ); // Get show/hide password toggles
282 |
283 | document.documentElement.classList.add( settings.initClass ); // Add class to HTML element to activate conditional CSS
284 |
285 | // Initialize password visibility defaults
286 | forEach(toggles, function (toggle, index) {
287 | var visibility = toggle.getAttribute('data-default');
288 | var pwID = toggle.getAttribute('data-x-ray');
289 | loadDefaultVisibility( toggle, visibility, pwID, settings );
290 | });
291 |
292 | // Listen for click events
293 | document.addEventListener('click', eventHandler, false);
294 |
295 | };
296 |
297 |
298 | //
299 | // Public APIs
300 | //
301 |
302 | return xray;
303 |
304 | });
--------------------------------------------------------------------------------
/dist/js/x-ray.min.js:
--------------------------------------------------------------------------------
1 | /*! x-ray v9.2.0 | (c) 2016 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/x-ray */
2 | !function(e,t){"function"==typeof define&&define.amd?define([],t(e)):"object"==typeof exports?module.exports=t(e):e.xray=t(e)}("undefined"!=typeof global?global:this.window||this.global,function(e){"use strict";var t,o,r={},l="querySelector"in document&&"addEventListener"in e&&"classList"in document.createElement("_"),c={selector:"[data-x-ray]",selectorShow:"[data-x-ray-show]",selectorHide:"[data-x-ray-hide]",toggleActiveClass:"active",initClass:"js-x-ray",callback:function(){}},n=function(e,t,o){if("[object Object]"===Object.prototype.toString.call(e))for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.call(o,e[r],r,e);else for(var l=0,c=e.length;l=0&&t.item(o)!==this;);return o>-1});e&&e!==document;e=e.parentNode)if(e.matches(t))return e;return null},i=function(e){n(e,function(e){var t=e.type.toLowerCase();"password"===t?e.type="text":"text"===t&&(e.type="password")})},u=function(e,t,o,r){var l=e.querySelector(r.selectorShow),c=e.querySelector(r.selectorHide),n=document.querySelectorAll(o);"show"===t?(i(n),c&&c.classList.add(r.toggleActiveClass)):l&&l.classList.add(r.toggleActiveClass)},d=function(e,t){var o=e.querySelector(".x-ray-show"),r=e.querySelector(".x-ray-hide");r&&r.classList.toggle(t.toggleActiveClass),o&&o.classList.toggle(t.toggleActiveClass)};r.runToggle=function(e,t,o,r){var l=a(l||c,o||{}),n=document.querySelectorAll(t);i(n),d(e,l),l.callback(e,t)};var y=function(e){var o=s(e.target,t.selector);o&&("a"!==o.tagName.toLowerCase()&&"button"!==o.tagName.toLowerCase()||e.preventDefault(),r.runToggle(o,o.getAttribute("data-x-ray"),t))};return r.destroy=function(){t&&(document.documentElement.classList.remove(t.initClass),document.removeEventListener("click",y,!1),o&&n(o,function(e){var o=document.querySelectorAll(e.getAttribute("data-x-ray")),r=e.querySelector(t.selectorShow),l=e.querySelector(t.selectorHide);n(o,function(e){e.type="password"}),r.classList.remove(t.toggleActiveClass),l.classList.remove(t.toggleActiveClass)}),t=null,o=null)},r.init=function(e){l&&(r.destroy(),t=a(c,e||{}),o=document.querySelectorAll(t.selector),document.documentElement.classList.add(t.initClass),n(o,function(e,o){var r=e.getAttribute("data-default"),l=e.getAttribute("data-x-ray");u(e,r,l,t)}),document.addEventListener("click",y,!1))},r});
--------------------------------------------------------------------------------
/docs/assets/css/custom.css:
--------------------------------------------------------------------------------
1 | @-webkit-viewport { width: device-width; zoom: 1.0; }
2 | @-moz-viewport { width: device-width; zoom: 1.0; }
3 | @-ms-viewport { width: device-width; zoom: 1.0; }
4 | @-o-viewport { width: device-width; zoom: 1.0; }
5 | @viewport { width: device-width; zoom: 1.0; }
6 |
7 | html { overflow-y: auto; }
8 |
9 | img, audio, video, canvas {
10 | max-width: 100%;
11 | height: auto;
12 | }
13 |
14 | /* Sets body width */
15 | .container {
16 | max-width: 40em;
17 | width: 88%;
18 | margin-left: auto;
19 | margin-right: auto;
20 | }
--------------------------------------------------------------------------------
/docs/dist/css/x-ray.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * x-ray v9.2.0: Toggle password visibility
3 | * (c) 2016 Chris Ferdinandi
4 | * MIT License
5 | * http://github.com/cferdinandi/x-ray
6 | */
7 |
8 | /* Hide show/hide labels by default */
9 | /* line 2, /Users/cferdinandi/Sites/x-ray/src/sass/components/_x-ray.scss */
10 | .x-ray,
11 | .x-ray-show,
12 | .x-ray-hide {
13 | display: none;
14 | visibility: hidden;
15 | }
16 |
17 | /* Display show/hide toggle when modern JS API's supported.
18 | * Display show/hide labels when active. */
19 | /* line 11, /Users/cferdinandi/Sites/x-ray/src/sass/components/_x-ray.scss */
20 | .js-x-ray .x-ray,
21 | .x-ray-show.active,
22 | .x-ray-hide.active {
23 | display: inline;
24 | visibility: visible;
25 | }
26 |
--------------------------------------------------------------------------------
/docs/dist/css/x-ray.min.css:
--------------------------------------------------------------------------------
1 | /*! x-ray v9.2.0 | (c) 2016 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/x-ray */
2 | .x-ray,.x-ray-hide,.x-ray-show{display:none;visibility:hidden}.js-x-ray .x-ray,.x-ray-hide.active,.x-ray-show.active{display:inline;visibility:visible}
--------------------------------------------------------------------------------
/docs/dist/js/x-ray.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * x-ray v9.2.0: Toggle password visibility
3 | * (c) 2016 Chris Ferdinandi
4 | * MIT License
5 | * http://github.com/cferdinandi/x-ray
6 | */
7 |
8 | (function (root, factory) {
9 | if ( typeof define === 'function' && define.amd ) {
10 | define([], factory(root));
11 | } else if ( typeof exports === 'object' ) {
12 | module.exports = factory(root);
13 | } else {
14 | root.xray = factory(root);
15 | }
16 | })(typeof global !== 'undefined' ? global : this.window || this.global, function (root) {
17 |
18 | 'use strict';
19 |
20 | //
21 | // Variables
22 | //
23 |
24 | var xray = {}; // Object for public APIs
25 | var supports = 'querySelector' in document && 'addEventListener' in root && 'classList' in document.createElement('_'); // Feature test
26 | var settings, toggles;
27 |
28 | // Default settings
29 | var defaults = {
30 | selector: '[data-x-ray]',
31 | selectorShow: '[data-x-ray-show]',
32 | selectorHide: '[data-x-ray-hide]',
33 | toggleActiveClass: 'active',
34 | initClass: 'js-x-ray',
35 | callback: function () {}
36 | };
37 |
38 |
39 | //
40 | // Methods
41 | //
42 |
43 | /**
44 | * A simple forEach() implementation for Arrays, Objects and NodeLists.
45 | * @private
46 | * @author Todd Motto
47 | * @link https://github.com/toddmotto/foreach
48 | * @param {Array|Object|NodeList} collection Collection of items to iterate
49 | * @param {Function} callback Callback function for each iteration
50 | * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
51 | */
52 | var forEach = function ( collection, callback, scope ) {
53 | if ( Object.prototype.toString.call( collection ) === '[object Object]' ) {
54 | for ( var prop in collection ) {
55 | if ( Object.prototype.hasOwnProperty.call( collection, prop ) ) {
56 | callback.call( scope, collection[prop], prop, collection );
57 | }
58 | }
59 | } else {
60 | for ( var i = 0, len = collection.length; i < len; i++ ) {
61 | callback.call( scope, collection[i], i, collection );
62 | }
63 | }
64 | };
65 |
66 | /**
67 | * Merge two or more objects. Returns a new object.
68 | * @private
69 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional]
70 | * @param {Object} objects The objects to merge together
71 | * @returns {Object} Merged values of defaults and options
72 | */
73 | var extend = function () {
74 |
75 | // Variables
76 | var extended = {};
77 | var deep = false;
78 | var i = 0;
79 | var length = arguments.length;
80 |
81 | // Check if a deep merge
82 | if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
83 | deep = arguments[0];
84 | i++;
85 | }
86 |
87 | // Merge the object into the extended object
88 | var merge = function (obj) {
89 | for ( var prop in obj ) {
90 | if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
91 | // If deep merge and property is an object, merge properties
92 | if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {
93 | extended[prop] = extend( true, extended[prop], obj[prop] );
94 | } else {
95 | extended[prop] = obj[prop];
96 | }
97 | }
98 | }
99 | };
100 |
101 | // Loop through each object and conduct a merge
102 | for ( ; i < length; i++ ) {
103 | var obj = arguments[i];
104 | merge(obj);
105 | }
106 |
107 | return extended;
108 |
109 | };
110 |
111 | /**
112 | * Get the closest matching element up the DOM tree.
113 | * @private
114 | * @param {Element} elem Starting element
115 | * @param {String} selector Selector to match against
116 | * @return {Boolean|Element} Returns null if not match found
117 | */
118 | var getClosest = function ( elem, selector ) {
119 |
120 | // Element.matches() polyfill
121 | if (!Element.prototype.matches) {
122 | Element.prototype.matches =
123 | Element.prototype.matchesSelector ||
124 | Element.prototype.mozMatchesSelector ||
125 | Element.prototype.msMatchesSelector ||
126 | Element.prototype.oMatchesSelector ||
127 | Element.prototype.webkitMatchesSelector ||
128 | function(s) {
129 | var matches = (this.document || this.ownerDocument).querySelectorAll(s),
130 | i = matches.length;
131 | while (--i >= 0 && matches.item(i) !== this) {}
132 | return i > -1;
133 | };
134 | }
135 |
136 | // Get closest match
137 | for ( ; elem && elem !== document; elem = elem.parentNode ) {
138 | if ( elem.matches( selector ) ) return elem;
139 | }
140 |
141 | return null;
142 |
143 | };
144 |
145 | /**
146 | * Toggle password visibility
147 | * @private
148 | * @param {NodeList} pws Password fields to toggle
149 | */
150 | var togglePW = function ( pws ) {
151 | forEach(pws, function (pw) {
152 | var pwType = pw.type.toLowerCase();
153 | if ( pwType === 'password' ) {
154 | pw.type = 'text';
155 | } else if ( pwType === 'text' ) {
156 | pw.type = 'password';
157 | }
158 | });
159 | };
160 |
161 | /**
162 | * Load default visibility
163 | * @private
164 | * @param {Element} toggle The element that toggles password visibility
165 | * @param {String} visibility Should the password be visible or hidden by default?
166 | * @param {String} pwSelector ID of the password field
167 | * @param {Object} settings
168 | */
169 | var loadDefaultVisibility = function ( toggle, visibility, pwSelector, settings ) {
170 | var showText = toggle.querySelector( settings.selectorShow );
171 | var hideText = toggle.querySelector( settings.selectorHide );
172 | var pws = document.querySelectorAll(pwSelector);
173 | if ( visibility === 'show' ) {
174 | togglePW(pws);
175 | if ( hideText ) {
176 | hideText.classList.add( settings.toggleActiveClass );
177 | }
178 | } else {
179 | if ( showText ) {
180 | showText.classList.add( settings.toggleActiveClass );
181 | }
182 | }
183 | };
184 |
185 | /**
186 | * Update toggle text
187 | * @private
188 | * @param {Element} toggle The element that toggles password visibility
189 | * @param {Object} settings
190 | */
191 | var updateToggleText = function ( toggle, settings ) {
192 | var showText = toggle.querySelector('.x-ray-show');
193 | var hideText = toggle.querySelector('.x-ray-hide');
194 | if ( hideText ) {
195 | hideText.classList.toggle( settings.toggleActiveClass );
196 | }
197 | if ( showText ) {
198 | showText.classList.toggle( settings.toggleActiveClass );
199 | }
200 | };
201 |
202 | /**
203 | * Show or hide password visibility
204 | * @public
205 | * @param {Element} toggle The element that toggles password visibility
206 | * @param {String} pwSelector The selector for the password fields
207 | * @param {Object} options
208 | * @param {Event} event
209 | */
210 | xray.runToggle = function ( toggle, pwSelector, options, event ) {
211 |
212 | // Selectors and variables
213 | var settings = extend( settings || defaults, options || {} ); // Merge user options with defaults
214 | var pws = document.querySelectorAll( pwSelector );
215 |
216 | togglePW( pws ); // Show/Hide password
217 | updateToggleText( toggle, settings ); // Change the toggle text
218 |
219 | settings.callback( toggle, pwSelector ); // Run callbacks after password visibility toggle
220 |
221 | };
222 |
223 | /**
224 | * Handle toggle click events
225 | * @private
226 | */
227 | var eventHandler = function (event) {
228 | var toggle = getClosest( event.target, settings.selector );
229 | if ( toggle ) {
230 | if ( toggle.tagName.toLowerCase() === 'a' || toggle.tagName.toLowerCase() === 'button' ) {
231 | event.preventDefault();
232 | }
233 | xray.runToggle( toggle, toggle.getAttribute('data-x-ray'), settings );
234 | }
235 | };
236 |
237 | /**
238 | * Destroy the current initialization.
239 | * @public
240 | */
241 | xray.destroy = function () {
242 | if ( !settings ) return;
243 | document.documentElement.classList.remove( settings.initClass );
244 | document.removeEventListener('click', eventHandler, false);
245 | if ( toggles ) {
246 | forEach( toggles, function ( toggle ) {
247 |
248 | // Get elements
249 | var pws = document.querySelectorAll( toggle.getAttribute('data-x-ray') );
250 | var showText = toggle.querySelector( settings.selectorShow );
251 | var hideText = toggle.querySelector( settings.selectorHide );
252 |
253 | // Reset to default password state
254 | forEach( pws, function ( pw ) {
255 | pw.type = 'password';
256 | });
257 | showText.classList.remove(settings.toggleActiveClass);
258 | hideText.classList.remove(settings.toggleActiveClass);
259 |
260 | });
261 | }
262 | settings = null;
263 | toggles = null;
264 | };
265 |
266 | /**
267 | * Initialize X-Ray
268 | * @public
269 | * @param {Object} options User settings
270 | */
271 | xray.init = function ( options ) {
272 |
273 | // feature test
274 | if ( !supports ) return;
275 |
276 | // Destroy any existing initializations
277 | xray.destroy();
278 |
279 | // Selectors and variables
280 | settings = extend( defaults, options || {} ); // Merge user options with defaults
281 | toggles = document.querySelectorAll( settings.selector ); // Get show/hide password toggles
282 |
283 | document.documentElement.classList.add( settings.initClass ); // Add class to HTML element to activate conditional CSS
284 |
285 | // Initialize password visibility defaults
286 | forEach(toggles, function (toggle, index) {
287 | var visibility = toggle.getAttribute('data-default');
288 | var pwID = toggle.getAttribute('data-x-ray');
289 | loadDefaultVisibility( toggle, visibility, pwID, settings );
290 | });
291 |
292 | // Listen for click events
293 | document.addEventListener('click', eventHandler, false);
294 |
295 | };
296 |
297 |
298 | //
299 | // Public APIs
300 | //
301 |
302 | return xray;
303 |
304 | });
--------------------------------------------------------------------------------
/docs/dist/js/x-ray.min.js:
--------------------------------------------------------------------------------
1 | /*! x-ray v9.2.0 | (c) 2016 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/x-ray */
2 | !function(e,t){"function"==typeof define&&define.amd?define([],t(e)):"object"==typeof exports?module.exports=t(e):e.xray=t(e)}("undefined"!=typeof global?global:this.window||this.global,function(e){"use strict";var t,o,r={},l="querySelector"in document&&"addEventListener"in e&&"classList"in document.createElement("_"),c={selector:"[data-x-ray]",selectorShow:"[data-x-ray-show]",selectorHide:"[data-x-ray-hide]",toggleActiveClass:"active",initClass:"js-x-ray",callback:function(){}},n=function(e,t,o){if("[object Object]"===Object.prototype.toString.call(e))for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.call(o,e[r],r,e);else for(var l=0,c=e.length;l=0&&t.item(o)!==this;);return o>-1});e&&e!==document;e=e.parentNode)if(e.matches(t))return e;return null},i=function(e){n(e,function(e){var t=e.type.toLowerCase();"password"===t?e.type="text":"text"===t&&(e.type="password")})},u=function(e,t,o,r){var l=e.querySelector(r.selectorShow),c=e.querySelector(r.selectorHide),n=document.querySelectorAll(o);"show"===t?(i(n),c&&c.classList.add(r.toggleActiveClass)):l&&l.classList.add(r.toggleActiveClass)},d=function(e,t){var o=e.querySelector(".x-ray-show"),r=e.querySelector(".x-ray-hide");r&&r.classList.toggle(t.toggleActiveClass),o&&o.classList.toggle(t.toggleActiveClass)};r.runToggle=function(e,t,o,r){var l=a(l||c,o||{}),n=document.querySelectorAll(t);i(n),d(e,l),l.callback(e,t)};var y=function(e){var o=s(e.target,t.selector);o&&("a"!==o.tagName.toLowerCase()&&"button"!==o.tagName.toLowerCase()||e.preventDefault(),r.runToggle(o,o.getAttribute("data-x-ray"),t))};return r.destroy=function(){t&&(document.documentElement.classList.remove(t.initClass),document.removeEventListener("click",y,!1),o&&n(o,function(e){var o=document.querySelectorAll(e.getAttribute("data-x-ray")),r=e.querySelector(t.selectorShow),l=e.querySelector(t.selectorHide);n(o,function(e){e.type="password"}),r.classList.remove(t.toggleActiveClass),l.classList.remove(t.toggleActiveClass)}),t=null,o=null)},r.init=function(e){l&&(r.destroy(),t=a(c,e||{}),o=document.querySelectorAll(t.selector),document.documentElement.classList.add(t.initClass),n(o,function(e,o){var r=e.getAttribute("data-default"),l=e.getAttribute("data-x-ray");u(e,r,l,t)}),document.addEventListener("click",y,!1))},r});
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | X-Ray
7 |
8 |
9 |
10 |
11 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |