├── 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 [](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 | });
--------------------------------------------------------------------------------