├── docs ├── dist │ ├── static.md │ ├── svg │ │ └── placeholder.svg │ ├── css │ │ ├── myplugin.min.css │ │ └── myplugin.css │ └── js │ │ ├── buoy.min.js │ │ └── buoy.js ├── assets │ └── css │ │ └── custom.css └── index.html ├── .travis.yml ├── .gitignore ├── package.json ├── LICENSE.md ├── dist └── js │ ├── buoy.min.js │ └── buoy.js ├── gulpfile.js ├── README.md └── src └── js └── buoy.js /docs/dist/static.md: -------------------------------------------------------------------------------- 1 | A place to include files that should be copied over as-is... -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_script: 5 | - npm install -g gulp 6 | script: gulp -------------------------------------------------------------------------------- /docs/dist/svg/placeholder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /docs/dist/css/myplugin.min.css: -------------------------------------------------------------------------------- 1 | /** buoy v0.0.1, by Chris Ferdinandi | http://github.com/cferdinandi/buoy | Licensed under MIT: http://gomakethings.com/mit/ */ 2 | -------------------------------------------------------------------------------- /docs/dist/css/myplugin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * buoy v0.0.1 3 | * A lightweight collection of helper methods for getting stuff done in native JavaScript., by Chris Ferdinandi. 4 | * http://github.com/cferdinandi/buoy 5 | * 6 | * Free to use under the MIT License. 7 | * http://gomakethings.com/mit/ 8 | */ 9 | 10 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buoy", 3 | "version": "1.1.0", 4 | "description": "A lightweight collection of helper methods for getting stuff done in native JavaScript.", 5 | "main": "./dist", 6 | "author": { 7 | "name": "Chris Ferdinandi", 8 | "url": "http://gomakethings.com" 9 | }, 10 | "license": "MIT", 11 | "repository": { 12 | "type": "git", 13 | "url": "http://github.com/cferdinandi/buoy" 14 | }, 15 | "devDependencies": { 16 | "gulp": "^3.9.0", 17 | "node-fs": "^0.1.7", 18 | "del": "^1.2.0", 19 | "lazypipe": "^0.2.4", 20 | "gulp-plumber": "^1.0.1", 21 | "gulp-flatten": "^0.0.4", 22 | "gulp-tap": "^0.1.3", 23 | "gulp-rename": "^1.2.2", 24 | "gulp-header": "^1.2.2", 25 | "gulp-footer": "^1.0.5", 26 | "gulp-watch": "^4.2.4", 27 | "gulp-livereload": "^3.8.0", 28 | "gulp-jshint": "^1.11.1", 29 | "jshint-stylish": "^2.0.1", 30 | "gulp-concat": "^2.6.0", 31 | "gulp-uglify": "^1.2.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | My Plugin 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 23 | 24 |
25 | 28 | 29 |

Content...

30 |
31 |
32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /docs/dist/js/buoy.min.js: -------------------------------------------------------------------------------- 1 | /** buoy v0.0.1, by Chris Ferdinandi | http://github.com/cferdinandi/buoy | Licensed under MIT: http://gomakethings.com/mit/ */ 2 | !function(t,e){"function"==typeof define&&define.amd?define([],e(t)):"object"==typeof exports?module.exports=e(t):t.buoy=e(t)}("undefined"!=typeof global?global:this.window||this.global,function(){"use strict";var t={};return t.forEach=function(t,e,r){if("[object Object]"===Object.prototype.toString.call(t))for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&e.call(r,t[n],n,t);else for(var o=0,a=t.length;a>o;o++)e.call(r,t[o],o,t)},t.extend=function(){var t={},e=function(e){for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r])};e(arguments[0]);for(var r=1;r=0?e:0},t.getClosest=function(t,e){for(var r=e.charAt(0);t&&t!==document;t=t.parentNode){if("."===r&&t.classList.contains(e.substr(1)))return t;if("#"===r&&t.id===e.substr(1))return t;if("["===r&&t.hasAttribute(e.substr(1,e.length-2)))return t;if(t.tagName.toLowerCase()===e)return t}return null},t.getParents=function(t,e){var r,n=[];for(e&&(r=e.charAt(0));t&&t!==document;t=t.parentNode)e?("."===r&&t.classList.contains(e.substr(1))&&n.push(t),"#"===r&&t.id===e.substr(1)&&n.push(t),"["===r&&t.hasAttribute(e.substr(1,e.length-1))&&n.push(t),t.tagName.toLowerCase()===e&&n.push(t)):n.push(t);return 0===n.length?null:n},t.getSiblings=function(t){for(var e=[],r=t.parentNode.firstChild;r;r=r.nextSibling)1===r.nodeType&&r!==t&&e.push(r);return e},t.getQueryString=function(t,e){var r=e?e:window.location.href,n=new RegExp("[?&]"+t+"=([^&#]*)","i"),o=n.exec(r);return o?o[1]:null},t}); -------------------------------------------------------------------------------- /dist/js/buoy.min.js: -------------------------------------------------------------------------------- 1 | /*! buoy v1.1.0 | (c) 2015 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/buoy */ 2 | !function(t,e){"function"==typeof define&&define.amd?define([],e(t)):"object"==typeof exports?module.exports=e(t):t.buoy=e(t)}("undefined"!=typeof global?global:this.window||this.global,function(t){"use strict";var e={};return e.ready=function(t){return"function"==typeof t?"complete"===document.readyState?t():void document.addEventListener("DOMContentLoaded",t,!1):void 0},e.forEach=function(t,e,n){if("[object Object]"===Object.prototype.toString.call(t))for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&e.call(n,t[r],r,t);else for(var o=0,s=t.length;s>o;o++)e.call(n,t[o],o,t)},e.extend=function(){var t={},n=!1,r=0,o=arguments.length;"[object Boolean]"===Object.prototype.toString.call(arguments[0])&&(n=arguments[0],r++);for(var s=function(r){for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(t[o]=n&&"[object Object]"===Object.prototype.toString.call(r[o])?e.extend(!0,t[o],r[o]):r[o])};o>r;r++){var i=arguments[r];s(i)}return t},e.getHeight=function(t){return Math.max(t.scrollHeight,t.offsetHeight,t.clientHeight)},e.getOffsetTop=function(t){var e=0;if(t.offsetParent)do e+=t.offsetTop,t=t.offsetParent;while(t);return e>=0?e:0},e.getClosest=function(t,e){var n,r,o=e.charAt(0),s="classList"in document.documentElement;for("["===o&&(e=e.substr(1,e.length-2),n=e.split("="),n.length>1&&(r=!0,n[1]=n[1].replace(/"/g,"").replace(/'/g,"")));t&&t!==document;t=t.parentNode){if("."===o)if(s){if(t.classList.contains(e.substr(1)))return t}else if(new RegExp("(^|\\s)"+e.substr(1)+"(\\s|$)").test(t.className))return t;if("#"===o&&t.id===e.substr(1))return t;if("["===o&&t.hasAttribute(n[0])){if(!r)return t;if(t.getAttribute(n[0])===n[1])return t}if(t.tagName.toLowerCase()===e)return t}return null},e.getParents=function(t,e){var n,r,o,s=[],i="classList"in document.documentElement;for(e&&(n=e.charAt(0),"["===n&&(e=e.substr(1,e.length-2),r=e.split("="),r.length>1&&(o=!0,r[1]=r[1].replace(/"/g,"").replace(/'/g,""))));t&&t!==document;t=t.parentNode)e?("."===n&&(i?t.classList.contains(e.substr(1))&&s.push(t):new RegExp("(^|\\s)"+e.substr(1)+"(\\s|$)").test(t.className)&&s.push(t)),"#"===n&&t.id===e.substr(1)&&s.push(t),"["===n&&t.hasAttribute(r[0])&&(o?t.getAttribute(r[0])===r[1]&&s.push(t):s.push(t)),t.tagName.toLowerCase()===e&&s.push(t)):s.push(t);return 0===s.length?null:s},e.getSiblings=function(t){for(var e=[],n=t.parentNode.firstChild;n;n=n.nextSibling)1===n.nodeType&&n!==t&&e.push(n);return e},e.getQueryString=function(t,e){var n=e?e:window.location.href,r=new RegExp("[?&]"+t+"=([^&#]*)","i"),o=r.exec(n);return o?o[1]:null},e}); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gulp Packages 3 | */ 4 | 5 | // General 6 | var gulp = require('gulp'); 7 | var fs = require('fs'); 8 | var del = require('del'); 9 | var lazypipe = require('lazypipe'); 10 | var plumber = require('gulp-plumber'); 11 | var flatten = require('gulp-flatten'); 12 | var tap = require('gulp-tap'); 13 | var rename = require('gulp-rename'); 14 | var header = require('gulp-header'); 15 | var footer = require('gulp-footer'); 16 | var watch = require('gulp-watch'); 17 | var livereload = require('gulp-livereload'); 18 | var package = require('./package.json'); 19 | 20 | // Scripts 21 | var jshint = require('gulp-jshint'); 22 | var stylish = require('jshint-stylish'); 23 | var concat = require('gulp-concat'); 24 | var uglify = require('gulp-uglify'); 25 | 26 | 27 | /** 28 | * Paths to project folders 29 | */ 30 | 31 | var paths = { 32 | input: 'src/**/*', 33 | output: 'dist/', 34 | scripts: { 35 | input: 'src/js/*', 36 | output: 'dist/js/' 37 | } 38 | }; 39 | 40 | 41 | /** 42 | * Template for banner to add to file headers 43 | */ 44 | 45 | var banner = { 46 | full : 47 | '/*!\n' + 48 | ' * <%= package.name %> v<%= package.version %>: <%= package.description %>\n' + 49 | ' * (c) ' + new Date().getFullYear() + ' <%= package.author.name %>\n' + 50 | ' * MIT License\n' + 51 | ' * <%= package.repository.url %>\n' + 52 | ' */\n\n', 53 | min : 54 | '/*!' + 55 | ' <%= package.name %> v<%= package.version %>' + 56 | ' | (c) ' + new Date().getFullYear() + ' <%= package.author.name %>' + 57 | ' | MIT License' + 58 | ' | <%= package.repository.url %>' + 59 | ' */\n' 60 | }; 61 | 62 | 63 | /** 64 | * Gulp Taks 65 | */ 66 | 67 | // Lint, minify, and concatenate scripts 68 | gulp.task('build:scripts', ['clean:dist'], function() { 69 | var jsTasks = lazypipe() 70 | .pipe(header, banner.full, { package : package }) 71 | .pipe(gulp.dest, paths.scripts.output) 72 | .pipe(rename, { suffix: '.min' }) 73 | .pipe(uglify) 74 | .pipe(header, banner.min, { package : package }) 75 | .pipe(gulp.dest, paths.scripts.output); 76 | 77 | return gulp.src(paths.scripts.input) 78 | .pipe(plumber()) 79 | .pipe(tap(function (file, t) { 80 | if ( file.isDirectory() ) { 81 | var name = file.relative + '.js'; 82 | return gulp.src(file.path + '/*.js') 83 | .pipe(concat(name)) 84 | .pipe(jsTasks()); 85 | } 86 | })) 87 | .pipe(jsTasks()); 88 | }); 89 | 90 | // Lint scripts 91 | gulp.task('lint:scripts', function () { 92 | return gulp.src(paths.scripts.input) 93 | .pipe(plumber()) 94 | .pipe(jshint()) 95 | .pipe(jshint.reporter('jshint-stylish')); 96 | }); 97 | 98 | // Remove prexisting content from output and test folders 99 | gulp.task('clean:dist', function () { 100 | del.sync([ 101 | paths.output 102 | ]); 103 | }); 104 | 105 | // Spin up livereload server and listen for file changes 106 | gulp.task('listen', function () { 107 | livereload.listen(); 108 | gulp.watch(paths.input).on('change', function(file) { 109 | gulp.start('default'); 110 | gulp.start('refresh'); 111 | }); 112 | }); 113 | 114 | // Run livereload after file change 115 | gulp.task('refresh', ['compile', 'docs'], function () { 116 | livereload.changed(); 117 | }); 118 | 119 | 120 | /** 121 | * Task Runners 122 | */ 123 | 124 | // Compile files (default) 125 | gulp.task('default', [ 126 | 'lint:scripts', 127 | 'clean:dist', 128 | 'build:scripts' 129 | ]); 130 | 131 | // Compile files when something changes 132 | gulp.task('watch', [ 133 | 'listen', 134 | 'default' 135 | ]); -------------------------------------------------------------------------------- /docs/dist/js/buoy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * buoy v0.0.1 3 | * A lightweight collection of helper methods for getting stuff done in native JavaScript., by Chris Ferdinandi. 4 | * http://github.com/cferdinandi/buoy 5 | * 6 | * Free to use under the MIT License. 7 | * http://gomakethings.com/mit/ 8 | */ 9 | 10 | (function (root, factory) { 11 | if ( typeof define === 'function' && define.amd ) { 12 | define([], factory(root)); 13 | } else if ( typeof exports === 'object' ) { 14 | module.exports = factory(root); 15 | } else { 16 | root.buoy = factory(root); 17 | } 18 | })(typeof global !== 'undefined' ? global : this.window || this.global, function (root) { 19 | 20 | 'use strict'; 21 | 22 | // Object for public APIs 23 | var buoy = {}; 24 | 25 | /** 26 | * A simple forEach() implementation for Arrays, Objects and NodeLists. 27 | * @author Todd Motto 28 | * @link https://github.com/toddmotto/foreach 29 | * @param {Array|Object|NodeList} collection Collection of items to iterate 30 | * @param {Function} callback Callback function for each iteration 31 | * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`) 32 | */ 33 | buoy.forEach = function (collection, callback, scope) { 34 | if (Object.prototype.toString.call(collection) === '[object Object]') { 35 | for (var prop in collection) { 36 | if (Object.prototype.hasOwnProperty.call(collection, prop)) { 37 | callback.call(scope, collection[prop], prop, collection); 38 | } 39 | } 40 | } else { 41 | for (var i = 0, len = collection.length; i < len; i++) { 42 | callback.call(scope, collection[i], i, collection); 43 | } 44 | } 45 | }; 46 | 47 | /** 48 | * Shallow merge two or more objects. Returns a new object. 49 | * @param {Object} defaults Default settings 50 | * @param {Object} options User options 51 | * @returns {Object} Merged values of defaults and options 52 | */ 53 | buoy.extend = function ( objects ) { 54 | var extended = {}; 55 | var merge = function (obj) { 56 | for (var prop in obj) { 57 | if (Object.prototype.hasOwnProperty.call(obj, prop)) { 58 | extended[prop] = obj[prop]; 59 | } 60 | } 61 | }; 62 | merge(arguments[0]); 63 | for (var i = 1; i < arguments.length; i++) { 64 | var obj = arguments[i]; 65 | merge(obj); 66 | } 67 | return extended; 68 | }; 69 | 70 | /** 71 | * Deep merge two or more objects. Returns a new object. 72 | * @param {Object} defaults Default settings 73 | * @param {Object} options User options 74 | * @returns {Object} Merged values of defaults and options 75 | */ 76 | buoy.deepExtend = function ( objects ) { 77 | var extended = {}; 78 | var merge = function (obj) { 79 | for (var prop in obj) { 80 | if (Object.prototype.hasOwnProperty.call(obj, prop)) { 81 | if ( Object.prototype.toString.call(obj[prop]) === '[object Object]' ) { 82 | extended[prop] = deepExtend(extended[prop], obj[prop]); 83 | } 84 | else { 85 | extended[prop] = obj[prop]; 86 | } 87 | } 88 | } 89 | }; 90 | merge(arguments[0]); 91 | for (var i = 1; i < arguments.length; i++) { 92 | var obj = arguments[i]; 93 | merge(obj); 94 | } 95 | return extended; 96 | }; 97 | 98 | /** 99 | * Get the height of an element. 100 | * @param {Node} elem The element to get the height of 101 | * @return {Number} The element's height in pixels 102 | */ 103 | buoy.getHeight = function ( elem ) { 104 | return Math.max( elem.scrollHeight, elem.offsetHeight, elem.clientHeight ); 105 | }; 106 | 107 | /** 108 | * Get an element's distance from the top of the Document. 109 | * @param {Node} elem The element 110 | * @return {Number} Distance from the top in pixels 111 | */ 112 | buy.getOffsetTop = function ( elem ) { 113 | var location = 0; 114 | if (elem.offsetParent) { 115 | do { 116 | location += elem.offsetTop; 117 | elem = elem.offsetParent; 118 | } while (elem); 119 | } 120 | return location >= 0 ? location : 0; 121 | }; 122 | 123 | /** 124 | * Get the closest matching element up the DOM tree. 125 | * @param {Element} elem Starting element 126 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 127 | * @return {Boolean|Element} Returns null if not match found 128 | */ 129 | buoy.getClosest = function (elem, selector) { 130 | 131 | var firstChar = selector.charAt(0); 132 | 133 | // Get closest match 134 | for ( ; elem && elem !== document; elem = elem.parentNode ) { 135 | 136 | // If selector is a class 137 | if ( firstChar === '.' ) { 138 | if ( elem.classList.contains( selector.substr(1) ) ) { 139 | return elem; 140 | } 141 | } 142 | 143 | // If selector is an ID 144 | if ( firstChar === '#' ) { 145 | if ( elem.id === selector.substr(1) ) { 146 | return elem; 147 | } 148 | } 149 | 150 | // If selector is a data attribute 151 | if ( firstChar === '[' ) { 152 | if ( elem.hasAttribute( selector.substr(1, selector.length - 2) ) ) { 153 | return elem; 154 | } 155 | } 156 | 157 | // If selector is a tag 158 | if ( elem.tagName.toLowerCase() === selector ) { 159 | return elem; 160 | } 161 | 162 | } 163 | 164 | return null; 165 | 166 | }; 167 | 168 | /** 169 | * Get an element's parents. 170 | * @param {Node} elem The element 171 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 172 | * @return {Array} An array of matching nodes 173 | */ 174 | buoy.getParents = function (elem, selector) { 175 | 176 | var parents = []; 177 | var firstChar; 178 | if ( selector ) { 179 | firstChar = selector.charAt(0); 180 | } 181 | 182 | // Get matches 183 | for ( ; elem && elem !== document; elem = elem.parentNode ) { 184 | if ( selector ) { 185 | 186 | // If selector is a class 187 | if ( firstChar === '.' ) { 188 | if ( elem.classList.contains( selector.substr(1) ) ) { 189 | parents.push( elem ); 190 | } 191 | } 192 | 193 | // If selector is an ID 194 | if ( firstChar === '#' ) { 195 | if ( elem.id === selector.substr(1) ) { 196 | parents.push( elem ); 197 | } 198 | } 199 | 200 | // If selector is a data attribute 201 | if ( firstChar === '[' ) { 202 | if ( elem.hasAttribute( selector.substr(1, selector.length - 1) ) ) { 203 | parents.push( elem ); 204 | } 205 | } 206 | 207 | // If selector is a tag 208 | if ( elem.tagName.toLowerCase() === selector ) { 209 | parents.push( elem ); 210 | } 211 | 212 | } else { 213 | parents.push( elem ); 214 | } 215 | 216 | } 217 | 218 | // Return parents if any exist 219 | if ( parents.length === 0 ) { 220 | return null; 221 | } else { 222 | return parents; 223 | } 224 | 225 | }; 226 | 227 | /** 228 | * Get an element's siblings. 229 | * @param {Node} elem The element 230 | * @return {Array} An array of sibling nodes 231 | */ 232 | buoy.getSiblings = function (elem) { 233 | var siblings = []; 234 | var sibling = elem.parentNode.firstChild; 235 | for ( ; sibling; sibling = sibling.nextSibling ) { 236 | if ( sibling.nodeType === 1 && sibling !== elem ) { 237 | siblings.push( sibling ); 238 | } 239 | } 240 | return siblings; 241 | }; 242 | 243 | /** 244 | * Get data from a URL query string. 245 | * @param {String} field The field to get from the URL 246 | * @param {String} url The URL to parse 247 | * @return {String} The field value 248 | */ 249 | buoy.getQueryString = function ( field, url ) { 250 | var href = url ? url : window.location.href; 251 | var reg = new RegExp( '[?&]' + field + '=([^&#]*)', 'i' ); 252 | var string = reg.exec(href); 253 | return string ? string[1] : null; 254 | }; 255 | 256 | // 257 | // Public APIs 258 | // 259 | 260 | return buoy; 261 | 262 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [DEPRECTATED] Buoy [![Build Status](https://travis-ci.org/cferdinandi/buoy.svg)](https://travis-ci.org/cferdinandi/buoy) 2 | 3 | ***Note:*** *This projects has been deprecated and is no longer supported.* 4 | 5 | A lightweight collection of helper methods for getting stuff done in native JavaScript. 6 | 7 | [Download Buoy](https://github.com/cferdinandi/buoy/archive/master.zip) 8 | 9 | 10 | 11 | ## Usage 12 | 13 | Compiled and production-ready code can be found in the `dist` directory. The `src` directory contains development code. 14 | 15 | Include Buoy on your site to begin using the methods in your scripts. 16 | 17 | ```html 18 | 19 | ``` 20 | 21 | ### Methods 22 | 23 | #### buoy.ready() 24 | 25 | Wait until the DOM is ready before executing code. 26 | 27 | ```js 28 | /** 29 | * @param {Function} fn The function to execute when the DOM is ready 30 | */ 31 | buoy.ready = function ( fn ) { 32 | 33 | // Sanity check 34 | if ( typeof fn !== 'function' ) return; 35 | 36 | // If document is already loaded, run method 37 | if ( document.readyState === 'complete' ) { 38 | return fn(); 39 | } 40 | 41 | // Otherwise, wait until document is loaded 42 | document.addEventListener( 'DOMContentLoaded', fn, false ); 43 | 44 | }; 45 | 46 | // Example 47 | buoy.ready(function () { 48 | console.log('The DOM will see you now.'); 49 | }); 50 | ``` 51 | 52 | #### buoy.forEach() 53 | 54 | A simple forEach() implementation for Arrays, Objects and NodeLists [by Todd Motto](https://github.com/toddmotto/foreach). 55 | 56 | ```js 57 | /** 58 | * @param {Array|Object|NodeList} collection Collection of items to iterate 59 | * @param {Function} callback Callback function for each iteration 60 | * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`) 61 | */ 62 | buoy.forEach(collection, callback, scope); 63 | 64 | // Examples 65 | buoy.forEach(['A', 'B', 'C', 'D'], function (value, index) { 66 | console.log(value); // A, B, C, D 67 | console.log(index); // 0, 1, 2, 3 68 | }); 69 | 70 | buoy.forEach({ name: 'Todd', location: 'UK' }, function (value, prop, obj) { 71 | console.log(value); // Todd, UK 72 | console.log(prop); // name, location 73 | console.log(obj); // { name: 'Todd', location: 'UK' }, { name: 'Todd', location: 'UK' } 74 | }); 75 | ``` 76 | 77 | #### buoy.extend() 78 | 79 | Merge two or more objects. Returns a new object. 80 | 81 | ```js 82 | /** 83 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 84 | * @param {Object} objects The objects to merge together 85 | * @returns {Object} Merged values of defaults and options 86 | */ 87 | buoy.extend( [deep], object1, object2, ... ); 88 | 89 | // Examples 90 | var object1 = { 91 | apple: 0, 92 | banana: { 93 | weight: 52, 94 | price: 100 95 | }, 96 | cherry: 97 97 | }; 98 | var object2 = { 99 | banana: { 100 | price: 200 101 | }, 102 | durian: 100 103 | }; 104 | var object3 = { 105 | apple: 'yum', 106 | pie: 3.214, 107 | applePie: true 108 | } 109 | 110 | var newObject = buoy.extend( object1, object2, object3 ); // Shallow extend 111 | var newObjectDeep = buoy.extend( true, object1, object2, object3 ); // Deep extend 112 | ``` 113 | 114 | #### buoy.getHeight() 115 | 116 | Get the height of an element. 117 | 118 | ```js 119 | /** 120 | * @param {Node} elem The element to get the height of 121 | * @return {Number} The element's height in pixels 122 | */ 123 | buoy.getHeight( elem ); 124 | 125 | // Example 126 | var elem = document.querySelector( '#example' ); 127 | var height = buoy.getHeight( elem ); // 42 128 | ``` 129 | 130 | #### buoy.getOffsetTop() 131 | 132 | Get an element's distance from the top of the Document. 133 | 134 | ```js 135 | /** 136 | * @param {Node} elem The element 137 | * @return {Number} Distance from the top in pixels 138 | */ 139 | buoy.getOffsetTop( elem ); 140 | 141 | // Example 142 | var elem = document.querySelector( '#example' ); 143 | buoy.getOffsetTop( elem ); // 133 144 | ``` 145 | 146 | #### buoy.getClosest() 147 | 148 | Get the closest matching element up the DOM tree. Match by class, ID, data attribute, or tag. 149 | 150 | ```js 151 | /** 152 | * @param {Element} elem Starting element 153 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 154 | * @return {Boolean|Element} Returns null if not match found 155 | */ 156 | buoy.getClosest( elem, selector ); 157 | 158 | // Examples 159 | var elem = document.querySelector( '#example' ); 160 | var closestClass = buoy.getClosest( elem, '.example' ); //
Some class
161 | var closestID = buoy.getClosest( elem, '#example-2' ); //
Some ID
162 | var closestAttribute = buoy.getClosest( elem, '[data-example]' ); //
Some data attribute
163 | var closestTag = buoy.getClosest( elem, 'p' ); //

Some tag

164 | ``` 165 | 166 | #### buoy.getParents 167 | 168 | Get an element's parent nodes. Optionally filter by class, ID, data attribute, or tag. 169 | 170 | ```js 171 | /** 172 | * @param {Node} elem The element 173 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) [optional] 174 | * @return {Array} An array of matching nodes 175 | */ 176 | buoy.getParents( elem, [selector] ); 177 | 178 | // Examples 179 | var elem = document.querySelector( '#example' ); 180 | var parents = buoy.getParents( elem ); // [
...
,
...
,
...
] 181 | var parentsWithClass = buoy.getParents( elem, '.example' ); // [
...
,
...
] 182 | var parentsWithID = buoy.getParents( elem, '#example-2' ); // [
...
] 183 | var parentsWithAttribute = buoy.getParents( elem, '[data-example]' ); // [
...
,
...
] 184 | var parentsWithTag = buoy.getParents( elem, 'div' ); // [
...
,
...
] 185 | ``` 186 | 187 | #### buoy.getSiblings() 188 | 189 | Get an element's sibling nodes. 190 | 191 | ```js 192 | /** 193 | * @param {Node} elem The element 194 | * @return {Array} An array of sibling nodes 195 | */ 196 | buoy.getSiblings( elem ); 197 | 198 | // Example 199 | var elem = document.querySelector( 'li#example' ); 200 | var siblings = buoy.getSiblings( elem ); // [
  • A
  • ,
  • B
  • ,
  • C
  • ] 201 | ``` 202 | 203 | #### buoy.getQueryString() 204 | 205 | Get data from a URL's query string. 206 | 207 | ```js 208 | /** 209 | * @param {String} field The field to get from the URL 210 | * @param {String} url The URL to parse 211 | * @return {String} The field value 212 | */ 213 | buoy.getQueryString = function ( field, url ); 214 | 215 | // Example 216 | var url = 'http://example.com?name=peter&superhero=spiderman'; 217 | var superhero = buoy.getQueryString( 'superhero', url ); // spiderman 218 | ``` 219 | 220 | 221 | ### Installing with Package Managers 222 | 223 | You can install Buoy with your favorite package manager. 224 | 225 | * **NPM:** `npm install cferdinandi/buoy` 226 | * **Bower:** `bower install https://github.com/cferdinandi/buoy.git` 227 | * **Component:** `component install cferdinandi/buoy` 228 | 229 | 230 | 231 | ## Browser Compatibility 232 | 233 | Buoy works in all modern browsers, and IE 8 and above. 234 | 235 | 236 | 237 | ## How to Contribute 238 | 239 | 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. 240 | 241 | 242 | 243 | ## Working with the Source Files 244 | 245 | 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. 246 | 247 | ### Dependencies 248 | Make sure these are installed first. 249 | 250 | * [Node.js](http://nodejs.org) 251 | * [Gulp](http://gulpjs.com) `sudo npm install -g gulp` 252 | 253 | ### Quick Start 254 | 255 | 1. In bash/terminal/command line, `cd` into your project directory. 256 | 2. Run `npm install` to install required files. 257 | 3. When it's done installing, run one of the task runners to get going: 258 | * `gulp` manually compiles files. 259 | * `gulp watch` automatically compiles files and applies changes using [LiveReload](http://livereload.com/). 260 | 261 | 262 | 263 | ## License 264 | 265 | The code is available under the [MIT License](LICENSE.md). 266 | -------------------------------------------------------------------------------- /src/js/buoy.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if ( typeof define === 'function' && define.amd ) { 3 | define([], factory(root)); 4 | } else if ( typeof exports === 'object' ) { 5 | module.exports = factory(root); 6 | } else { 7 | root.buoy = factory(root); 8 | } 9 | })(typeof global !== 'undefined' ? global : this.window || this.global, function (root) { 10 | 11 | 'use strict'; 12 | 13 | // Object for public APIs 14 | var buoy = {}; 15 | 16 | 17 | // 18 | // Methods 19 | // 20 | 21 | /** 22 | * Wait until the DOM is ready before executing code 23 | * @param {Function} fn The function to execute when the DOM is ready 24 | */ 25 | buoy.ready = function ( fn ) { 26 | 27 | // Sanity check 28 | if ( typeof fn !== 'function' ) return; 29 | 30 | // If document is already loaded, run method 31 | if ( document.readyState === 'complete' ) { 32 | return fn(); 33 | } 34 | 35 | // Otherwise, wait until document is loaded 36 | document.addEventListener( 'DOMContentLoaded', fn, false ); 37 | 38 | }; 39 | 40 | /** 41 | * A simple forEach() implementation for Arrays, Objects and NodeLists. 42 | * @author Todd Motto 43 | * @link https://github.com/toddmotto/foreach 44 | * @param {Array|Object|NodeList} collection Collection of items to iterate 45 | * @param {Function} callback Callback function for each iteration 46 | * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`) 47 | */ 48 | buoy.forEach = function ( collection, callback, scope ) { 49 | if ( Object.prototype.toString.call( collection ) === '[object Object]' ) { 50 | for ( var prop in collection ) { 51 | if ( Object.prototype.hasOwnProperty.call( collection, prop ) ) { 52 | callback.call( scope, collection[prop], prop, collection ); 53 | } 54 | } 55 | } else { 56 | for ( var i = 0, len = collection.length; i < len; i++ ) { 57 | callback.call( scope, collection[i], i, collection ); 58 | } 59 | } 60 | }; 61 | 62 | /** 63 | * Merge two or more objects. Returns a new object. 64 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 65 | * @param {Object} objects The objects to merge together 66 | * @returns {Object} Merged values of defaults and options 67 | */ 68 | buoy.extend = function () { 69 | 70 | // Variables 71 | var extended = {}; 72 | var deep = false; 73 | var i = 0; 74 | var length = arguments.length; 75 | 76 | // Check if a deep merge 77 | if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { 78 | deep = arguments[0]; 79 | i++; 80 | } 81 | 82 | // Merge the object into the extended object 83 | var merge = function (obj) { 84 | for ( var prop in obj ) { 85 | if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) { 86 | // If deep merge and property is an object, merge properties 87 | if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) { 88 | extended[prop] = buoy.extend( true, extended[prop], obj[prop] ); 89 | } else { 90 | extended[prop] = obj[prop]; 91 | } 92 | } 93 | } 94 | }; 95 | 96 | // Loop through each object and conduct a merge 97 | for ( ; i < length; i++ ) { 98 | var obj = arguments[i]; 99 | merge(obj); 100 | } 101 | 102 | return extended; 103 | 104 | }; 105 | 106 | /** 107 | * Get the height of an element. 108 | * @param {Node} elem The element to get the height of 109 | * @return {Number} The element's height in pixels 110 | */ 111 | buoy.getHeight = function ( elem ) { 112 | return Math.max( elem.scrollHeight, elem.offsetHeight, elem.clientHeight ); 113 | }; 114 | 115 | /** 116 | * Get an element's distance from the top of the Document. 117 | * @param {Node} elem The element 118 | * @return {Number} Distance from the top in pixels 119 | */ 120 | buoy.getOffsetTop = function ( elem ) { 121 | var location = 0; 122 | if (elem.offsetParent) { 123 | do { 124 | location += elem.offsetTop; 125 | elem = elem.offsetParent; 126 | } while (elem); 127 | } 128 | return location >= 0 ? location : 0; 129 | }; 130 | 131 | /** 132 | * Get the closest matching element up the DOM tree. 133 | * @param {Element} elem Starting element 134 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 135 | * @return {Boolean|Element} Returns null if not match found 136 | */ 137 | buoy.getClosest = function ( elem, selector ) { 138 | 139 | // Variables 140 | var firstChar = selector.charAt(0); 141 | var supports = 'classList' in document.documentElement; 142 | var attribute, value; 143 | 144 | // If selector is a data attribute, split attribute from value 145 | if ( firstChar === '[' ) { 146 | selector = selector.substr(1, selector.length - 2); 147 | attribute = selector.split( '=' ); 148 | 149 | if ( attribute.length > 1 ) { 150 | value = true; 151 | attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' ); 152 | } 153 | } 154 | 155 | // Get closest match 156 | for ( ; elem && elem !== document; elem = elem.parentNode ) { 157 | 158 | // If selector is a class 159 | if ( firstChar === '.' ) { 160 | if ( supports ) { 161 | if ( elem.classList.contains( selector.substr(1) ) ) { 162 | return elem; 163 | } 164 | } else { 165 | if ( new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test( elem.className ) ) { 166 | return elem; 167 | } 168 | } 169 | } 170 | 171 | // If selector is an ID 172 | if ( firstChar === '#' ) { 173 | if ( elem.id === selector.substr(1) ) { 174 | return elem; 175 | } 176 | } 177 | 178 | // If selector is a data attribute 179 | if ( firstChar === '[' ) { 180 | if ( elem.hasAttribute( attribute[0] ) ) { 181 | if ( value ) { 182 | if ( elem.getAttribute( attribute[0] ) === attribute[1] ) { 183 | return elem; 184 | } 185 | } else { 186 | return elem; 187 | } 188 | } 189 | } 190 | 191 | // If selector is a tag 192 | if ( elem.tagName.toLowerCase() === selector ) { 193 | return elem; 194 | } 195 | 196 | } 197 | 198 | return null; 199 | 200 | }; 201 | 202 | /** 203 | * Get an element's parents. 204 | * @param {Node} elem The element 205 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 206 | * @return {Array} An array of matching nodes 207 | */ 208 | buoy.getParents = function ( elem, selector ) { 209 | 210 | // Variables 211 | var parents = []; 212 | var supports = 'classList' in document.documentElement; 213 | var firstChar, attribute, value; 214 | 215 | // If selector is a data attribute, split attribute from value 216 | if ( selector ) { 217 | firstChar = selector.charAt(0); 218 | if ( firstChar === '[' ) { 219 | selector = selector.substr(1, selector.length - 2); 220 | attribute = selector.split( '=' ); 221 | 222 | if ( attribute.length > 1 ) { 223 | value = true; 224 | attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' ); 225 | } 226 | } 227 | } 228 | 229 | // Get matches 230 | for ( ; elem && elem !== document; elem = elem.parentNode ) { 231 | if ( selector ) { 232 | 233 | // If selector is a class 234 | if ( firstChar === '.' ) { 235 | if ( supports ) { 236 | if ( elem.classList.contains( selector.substr(1) ) ) { 237 | parents.push( elem ); 238 | } 239 | } else { 240 | if ( new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test( elem.className ) ) { 241 | parents.push( elem ); 242 | } 243 | } 244 | } 245 | 246 | // If selector is an ID 247 | if ( firstChar === '#' ) { 248 | if ( elem.id === selector.substr(1) ) { 249 | parents.push( elem ); 250 | } 251 | } 252 | 253 | // If selector is a data attribute 254 | if ( firstChar === '[' ) { 255 | if ( elem.hasAttribute( attribute[0] ) ) { 256 | if ( value ) { 257 | if ( elem.getAttribute( attribute[0] ) === attribute[1] ) { 258 | parents.push( elem ); 259 | } 260 | } else { 261 | parents.push( elem ); 262 | } 263 | } 264 | } 265 | 266 | // If selector is a tag 267 | if ( elem.tagName.toLowerCase() === selector ) { 268 | parents.push( elem ); 269 | } 270 | 271 | } else { 272 | parents.push( elem ); 273 | } 274 | 275 | } 276 | 277 | // Return parents if any exist 278 | if ( parents.length === 0 ) { 279 | return null; 280 | } else { 281 | return parents; 282 | } 283 | 284 | }; 285 | 286 | /** 287 | * Get an element's siblings. 288 | * @param {Node} elem The element 289 | * @return {Array} An array of sibling nodes 290 | */ 291 | buoy.getSiblings = function ( elem ) { 292 | 293 | // Variables 294 | var siblings = []; 295 | var sibling = elem.parentNode.firstChild; 296 | 297 | // Loop through all sibling nodes 298 | for ( ; sibling; sibling = sibling.nextSibling ) { 299 | if ( sibling.nodeType === 1 && sibling !== elem ) { 300 | siblings.push( sibling ); 301 | } 302 | } 303 | 304 | return siblings; 305 | 306 | }; 307 | 308 | /** 309 | * Get data from a URL query string. 310 | * @param {String} field The field to get from the URL 311 | * @param {String} url The URL to parse 312 | * @return {String} The field value 313 | */ 314 | buoy.getQueryString = function ( field, url ) { 315 | var href = url ? url : window.location.href; 316 | var reg = new RegExp( '[?&]' + field + '=([^&#]*)', 'i' ); 317 | var string = reg.exec(href); 318 | return string ? string[1] : null; 319 | }; 320 | 321 | 322 | // 323 | // Public APIs 324 | // 325 | 326 | return buoy; 327 | 328 | }); -------------------------------------------------------------------------------- /dist/js/buoy.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * buoy v1.1.0: A lightweight collection of helper methods for getting stuff done in native JavaScript. 3 | * (c) 2015 Chris Ferdinandi 4 | * MIT License 5 | * http://github.com/cferdinandi/buoy 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.buoy = factory(root); 15 | } 16 | })(typeof global !== 'undefined' ? global : this.window || this.global, function (root) { 17 | 18 | 'use strict'; 19 | 20 | // Object for public APIs 21 | var buoy = {}; 22 | 23 | 24 | // 25 | // Methods 26 | // 27 | 28 | /** 29 | * Wait until the DOM is ready before executing code 30 | * @param {Function} fn The function to execute when the DOM is ready 31 | */ 32 | buoy.ready = function ( fn ) { 33 | 34 | // Sanity check 35 | if ( typeof fn !== 'function' ) return; 36 | 37 | // If document is already loaded, run method 38 | if ( document.readyState === 'complete' ) { 39 | return fn(); 40 | } 41 | 42 | // Otherwise, wait until document is loaded 43 | document.addEventListener( 'DOMContentLoaded', fn, false ); 44 | 45 | }; 46 | 47 | /** 48 | * A simple forEach() implementation for Arrays, Objects and NodeLists. 49 | * @author Todd Motto 50 | * @link https://github.com/toddmotto/foreach 51 | * @param {Array|Object|NodeList} collection Collection of items to iterate 52 | * @param {Function} callback Callback function for each iteration 53 | * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`) 54 | */ 55 | buoy.forEach = function ( collection, callback, scope ) { 56 | if ( Object.prototype.toString.call( collection ) === '[object Object]' ) { 57 | for ( var prop in collection ) { 58 | if ( Object.prototype.hasOwnProperty.call( collection, prop ) ) { 59 | callback.call( scope, collection[prop], prop, collection ); 60 | } 61 | } 62 | } else { 63 | for ( var i = 0, len = collection.length; i < len; i++ ) { 64 | callback.call( scope, collection[i], i, collection ); 65 | } 66 | } 67 | }; 68 | 69 | /** 70 | * Merge two or more objects. Returns a new object. 71 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 72 | * @param {Object} objects The objects to merge together 73 | * @returns {Object} Merged values of defaults and options 74 | */ 75 | buoy.extend = function () { 76 | 77 | // Variables 78 | var extended = {}; 79 | var deep = false; 80 | var i = 0; 81 | var length = arguments.length; 82 | 83 | // Check if a deep merge 84 | if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { 85 | deep = arguments[0]; 86 | i++; 87 | } 88 | 89 | // Merge the object into the extended object 90 | var merge = function (obj) { 91 | for ( var prop in obj ) { 92 | if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) { 93 | // If deep merge and property is an object, merge properties 94 | if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) { 95 | extended[prop] = buoy.extend( true, extended[prop], obj[prop] ); 96 | } else { 97 | extended[prop] = obj[prop]; 98 | } 99 | } 100 | } 101 | }; 102 | 103 | // Loop through each object and conduct a merge 104 | for ( ; i < length; i++ ) { 105 | var obj = arguments[i]; 106 | merge(obj); 107 | } 108 | 109 | return extended; 110 | 111 | }; 112 | 113 | /** 114 | * Get the height of an element. 115 | * @param {Node} elem The element to get the height of 116 | * @return {Number} The element's height in pixels 117 | */ 118 | buoy.getHeight = function ( elem ) { 119 | return Math.max( elem.scrollHeight, elem.offsetHeight, elem.clientHeight ); 120 | }; 121 | 122 | /** 123 | * Get an element's distance from the top of the Document. 124 | * @param {Node} elem The element 125 | * @return {Number} Distance from the top in pixels 126 | */ 127 | buoy.getOffsetTop = function ( elem ) { 128 | var location = 0; 129 | if (elem.offsetParent) { 130 | do { 131 | location += elem.offsetTop; 132 | elem = elem.offsetParent; 133 | } while (elem); 134 | } 135 | return location >= 0 ? location : 0; 136 | }; 137 | 138 | /** 139 | * Get the closest matching element up the DOM tree. 140 | * @param {Element} elem Starting element 141 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 142 | * @return {Boolean|Element} Returns null if not match found 143 | */ 144 | buoy.getClosest = function ( elem, selector ) { 145 | 146 | // Variables 147 | var firstChar = selector.charAt(0); 148 | var supports = 'classList' in document.documentElement; 149 | var attribute, value; 150 | 151 | // If selector is a data attribute, split attribute from value 152 | if ( firstChar === '[' ) { 153 | selector = selector.substr(1, selector.length - 2); 154 | attribute = selector.split( '=' ); 155 | 156 | if ( attribute.length > 1 ) { 157 | value = true; 158 | attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' ); 159 | } 160 | } 161 | 162 | // Get closest match 163 | for ( ; elem && elem !== document; elem = elem.parentNode ) { 164 | 165 | // If selector is a class 166 | if ( firstChar === '.' ) { 167 | if ( supports ) { 168 | if ( elem.classList.contains( selector.substr(1) ) ) { 169 | return elem; 170 | } 171 | } else { 172 | if ( new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test( elem.className ) ) { 173 | return elem; 174 | } 175 | } 176 | } 177 | 178 | // If selector is an ID 179 | if ( firstChar === '#' ) { 180 | if ( elem.id === selector.substr(1) ) { 181 | return elem; 182 | } 183 | } 184 | 185 | // If selector is a data attribute 186 | if ( firstChar === '[' ) { 187 | if ( elem.hasAttribute( attribute[0] ) ) { 188 | if ( value ) { 189 | if ( elem.getAttribute( attribute[0] ) === attribute[1] ) { 190 | return elem; 191 | } 192 | } else { 193 | return elem; 194 | } 195 | } 196 | } 197 | 198 | // If selector is a tag 199 | if ( elem.tagName.toLowerCase() === selector ) { 200 | return elem; 201 | } 202 | 203 | } 204 | 205 | return null; 206 | 207 | }; 208 | 209 | /** 210 | * Get an element's parents. 211 | * @param {Node} elem The element 212 | * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 213 | * @return {Array} An array of matching nodes 214 | */ 215 | buoy.getParents = function ( elem, selector ) { 216 | 217 | // Variables 218 | var parents = []; 219 | var supports = 'classList' in document.documentElement; 220 | var firstChar, attribute, value; 221 | 222 | // If selector is a data attribute, split attribute from value 223 | if ( selector ) { 224 | firstChar = selector.charAt(0); 225 | if ( firstChar === '[' ) { 226 | selector = selector.substr(1, selector.length - 2); 227 | attribute = selector.split( '=' ); 228 | 229 | if ( attribute.length > 1 ) { 230 | value = true; 231 | attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' ); 232 | } 233 | } 234 | } 235 | 236 | // Get matches 237 | for ( ; elem && elem !== document; elem = elem.parentNode ) { 238 | if ( selector ) { 239 | 240 | // If selector is a class 241 | if ( firstChar === '.' ) { 242 | if ( supports ) { 243 | if ( elem.classList.contains( selector.substr(1) ) ) { 244 | parents.push( elem ); 245 | } 246 | } else { 247 | if ( new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test( elem.className ) ) { 248 | parents.push( elem ); 249 | } 250 | } 251 | } 252 | 253 | // If selector is an ID 254 | if ( firstChar === '#' ) { 255 | if ( elem.id === selector.substr(1) ) { 256 | parents.push( elem ); 257 | } 258 | } 259 | 260 | // If selector is a data attribute 261 | if ( firstChar === '[' ) { 262 | if ( elem.hasAttribute( attribute[0] ) ) { 263 | if ( value ) { 264 | if ( elem.getAttribute( attribute[0] ) === attribute[1] ) { 265 | parents.push( elem ); 266 | } 267 | } else { 268 | parents.push( elem ); 269 | } 270 | } 271 | } 272 | 273 | // If selector is a tag 274 | if ( elem.tagName.toLowerCase() === selector ) { 275 | parents.push( elem ); 276 | } 277 | 278 | } else { 279 | parents.push( elem ); 280 | } 281 | 282 | } 283 | 284 | // Return parents if any exist 285 | if ( parents.length === 0 ) { 286 | return null; 287 | } else { 288 | return parents; 289 | } 290 | 291 | }; 292 | 293 | /** 294 | * Get an element's siblings. 295 | * @param {Node} elem The element 296 | * @return {Array} An array of sibling nodes 297 | */ 298 | buoy.getSiblings = function ( elem ) { 299 | 300 | // Variables 301 | var siblings = []; 302 | var sibling = elem.parentNode.firstChild; 303 | 304 | // Loop through all sibling nodes 305 | for ( ; sibling; sibling = sibling.nextSibling ) { 306 | if ( sibling.nodeType === 1 && sibling !== elem ) { 307 | siblings.push( sibling ); 308 | } 309 | } 310 | 311 | return siblings; 312 | 313 | }; 314 | 315 | /** 316 | * Get data from a URL query string. 317 | * @param {String} field The field to get from the URL 318 | * @param {String} url The URL to parse 319 | * @return {String} The field value 320 | */ 321 | buoy.getQueryString = function ( field, url ) { 322 | var href = url ? url : window.location.href; 323 | var reg = new RegExp( '[?&]' + field + '=([^&#]*)', 'i' ); 324 | var string = reg.exec(href); 325 | return string ? string[1] : null; 326 | }; 327 | 328 | 329 | // 330 | // Public APIs 331 | // 332 | 333 | return buoy; 334 | 335 | }); --------------------------------------------------------------------------------