Initial commit
This commit is contained in:
commit
e2b3f924a5
|
@ -0,0 +1,10 @@
|
|||
# Node
|
||||
node_modules
|
||||
test/results
|
||||
test/coverage
|
||||
|
||||
## OS X
|
||||
.DS_Store
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
|
@ -0,0 +1,7 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
before_script:
|
||||
- npm install -g gulp
|
||||
script: gulp
|
|
@ -0,0 +1,122 @@
|
|||
# Sticky Footer [![Build Status](https://travis-ci.org/cferdinandi/sticky-footer.svg)](https://travis-ci.org/cferdinandi/sticky-footer)
|
||||
Responsive sticky footers that adjust dynamically as the screen size changes.
|
||||
|
||||
[Download Sticky Footer](https://github.com/cferdinandi/sticky-footer/archive/master.zip) / [View the demo](http://cferdinandi.github.io/sticky-footer/).
|
||||
|
||||
**In This Documentation**
|
||||
|
||||
1. [Getting Started](#getting-started)
|
||||
2. [Installing with Package Managers](#installing-with-package-managers)
|
||||
3. [Options & Settings](#options-and-settings)
|
||||
4. [Browser Compatibility](#browser-compatibility)
|
||||
5. [How to Contribute](#how-to-contribute)
|
||||
6. [License](#license)
|
||||
7. [Changelog](#changelog)
|
||||
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
Compiled and production-ready code can be found in the `dist` directory. The `src` directory contains development code. Unit tests are located in the `test` directory.
|
||||
|
||||
### 1. Include Sticky Footer on your site.
|
||||
|
||||
```html
|
||||
<script src="dist/js/classList.js"></script>
|
||||
<script src="dist/js/bind-polyfill.js"></script>
|
||||
<script src="dist/js/sticky-footer.js"></script>
|
||||
```
|
||||
|
||||
Sticky Footer requires [classList.js](https://github.com/eligrey/classList.js) and `bind-polyfill.js`, polyfills that extend ECMAScript 5 API support to more browsers.
|
||||
|
||||
### 2. Add the markup to your HTML.
|
||||
|
||||
```html
|
||||
<div data-sticky-wrap>
|
||||
Body content
|
||||
</div>
|
||||
<div data-sticky-footer>
|
||||
Footer content
|
||||
</div>
|
||||
```
|
||||
|
||||
Add the `data-sticky-wrap` attribute to a parent `<div>` that contains all of your page content. Add the `data-sticky-footer` attribute to the parent `<div>` that contains all of your footer content.
|
||||
|
||||
### 3. Initialize Sticky Footer.
|
||||
|
||||
```html
|
||||
<script>
|
||||
stickyFooter.init();
|
||||
</script>
|
||||
```
|
||||
|
||||
In the footer of your page, after the content, initialize Sticky Footer. And that's it, you're done. Nice work!
|
||||
|
||||
|
||||
|
||||
## Installing with Package Managers
|
||||
|
||||
You can install NAMEPSACE-UP with your favorite package manager.
|
||||
|
||||
* **NPM:** `npm install cferdinandi/sticky-footer`
|
||||
* **Bower:** `bower install https://github.com/cferdinandi/sticky-footer.git`
|
||||
* **Component:** `component install cferdinandi/sticky-footer`
|
||||
|
||||
|
||||
|
||||
## Options and Settings
|
||||
|
||||
Sticky Footer includes smart defaults and works right out of the box. But if you want to customize things, it also has a robust API that provides multiple ways for you to adjust the default options and settings.
|
||||
|
||||
### Global Settings
|
||||
|
||||
You can pass callbacks into Sticky Footer through the `init()` function:
|
||||
|
||||
```javascript
|
||||
sticky-footer.init({
|
||||
callbacks: {
|
||||
before: function () {}, // Runs before the footer is stuck
|
||||
after: function () {} // Runs after the footer is stuck
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Use Sticky Footer events in your own scripts
|
||||
|
||||
You can also call Sticky Footer events in your own scripts.
|
||||
|
||||
#### stickyFooter.destroy()
|
||||
Destroy the current `stickyFooter.init()`.
|
||||
|
||||
```javascript
|
||||
stickyFooter.destroy();
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
Sticky Footer works in all modern browsers, and IE 9 and above.
|
||||
|
||||
Sticky Footer is built with modern JavaScript APIs, and uses progressive enhancement. If the JavaScript file fails to load, or if your site is viewed on older and less capable browsers, footers will simply float up against the content like they normally would.
|
||||
|
||||
|
||||
|
||||
## How to Contribute
|
||||
|
||||
In lieu of a formal style guide, take care to maintain the existing coding style. Don't forget to update the version number, the changelog (in the `readme.md` file), and when applicable, the documentation.
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Sticky Footer is licensed under the [MIT License](http://gomakethings.com/mit/).
|
||||
|
||||
|
||||
|
||||
## Changelog
|
||||
|
||||
Sticky Footer uses [semantic versioning](http://semver.org/).
|
||||
|
||||
* v1.0.0 - August 15, 2014
|
||||
* Initial release.
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* sticky-footer v1.0.0
|
||||
* Responsive sticky footers, by Chris Ferdinandi.
|
||||
* http://github.com/cferdinandi/sticky-footer
|
||||
*
|
||||
* Free to use under the MIT License.
|
||||
* http://gomakethings.com/mit/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Polyfill Function.prototype.bind support for otherwise ECMA Script 5 compliant browsers
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
|
||||
*/
|
||||
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1);
|
||||
var fToBind = this;
|
||||
fNOP = function () {};
|
||||
fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/** sticky-footer v1.0.0, by Chris Ferdinandi | http://github.com/cferdinandi/sticky-footer | Licensed under MIT: http://gomakethings.com/mit/ */
|
||||
Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var o=Array.prototype.slice.call(arguments,1),n=this;return fNOP=function(){},fBound=function(){return n.apply(this instanceof fNOP&&t?this:t,o.concat(Array.prototype.slice.call(arguments)))},fNOP.prototype=this.prototype,fBound.prototype=new fNOP,fBound});
|
|
@ -0,0 +1,176 @@
|
|||
/**
|
||||
* sticky-footer v1.0.0
|
||||
* Responsive sticky footers, by Chris Ferdinandi.
|
||||
* http://github.com/cferdinandi/sticky-footer
|
||||
*
|
||||
* Free to use under the MIT License.
|
||||
* http://gomakethings.com/mit/
|
||||
*/
|
||||
|
||||
/*
|
||||
* classList.js: Cross-browser full element.classList implementation.
|
||||
* 2014-01-31
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
* Public Domain.
|
||||
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
*/
|
||||
|
||||
/*global self, document, DOMException */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
|
||||
|
||||
if ("document" in self && !("classList" in document.createElement("_"))) {
|
||||
|
||||
(function (view) {
|
||||
|
||||
"use strict";
|
||||
|
||||
if (!('Element' in view)) return;
|
||||
|
||||
var
|
||||
classListProp = "classList",
|
||||
protoProp = "prototype",
|
||||
elemCtrProto = view.Element[protoProp],
|
||||
objCtr = Object,
|
||||
strTrim = String[protoProp].trim || function () {
|
||||
return this.replace(/^\s+|\s+$/g, "");
|
||||
},
|
||||
arrIndexOf = Array[protoProp].indexOf || function (item) {
|
||||
var
|
||||
i = 0,
|
||||
len = this.length;
|
||||
for (; i < len; i++) {
|
||||
if (i in this && this[i] === item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
// Vendors: please allow content code to instantiate DOMExceptions
|
||||
DOMEx = function (type, message) {
|
||||
this.name = type;
|
||||
this.code = DOMException[type];
|
||||
this.message = message;
|
||||
},
|
||||
checkTokenAndGetIndex = function (classList, token) {
|
||||
if (token === "") {
|
||||
throw new DOMEx(
|
||||
"SYNTAX_ERR",
|
||||
"An invalid or illegal string was specified"
|
||||
);
|
||||
}
|
||||
if (/\s/.test(token)) {
|
||||
throw new DOMEx(
|
||||
"INVALID_CHARACTER_ERR",
|
||||
"String contains an invalid character"
|
||||
);
|
||||
}
|
||||
return arrIndexOf.call(classList, token);
|
||||
},
|
||||
ClassList = function (elem) {
|
||||
var
|
||||
trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
|
||||
classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
|
||||
i = 0,
|
||||
len = classes.length;
|
||||
for (; i < len; i++) {
|
||||
this.push(classes[i]);
|
||||
}
|
||||
this._updateClassName = function () {
|
||||
elem.setAttribute("class", this.toString());
|
||||
};
|
||||
},
|
||||
classListProto = ClassList[protoProp] = [],
|
||||
classListGetter = function () {
|
||||
return new ClassList(this);
|
||||
};
|
||||
// Most DOMException implementations don't allow calling DOMException's toString()
|
||||
// on non-DOMExceptions. Error's toString() is sufficient here.
|
||||
DOMEx[protoProp] = Error[protoProp];
|
||||
classListProto.item = function (i) {
|
||||
return this[i] || null;
|
||||
};
|
||||
classListProto.contains = function (token) {
|
||||
token += "";
|
||||
return checkTokenAndGetIndex(this, token) !== -1;
|
||||
};
|
||||
classListProto.add = function () {
|
||||
var
|
||||
tokens = arguments,
|
||||
i = 0,
|
||||
l = tokens.length,
|
||||
token,
|
||||
updated = false;
|
||||
do {
|
||||
token = tokens[i] + "";
|
||||
if (checkTokenAndGetIndex(this, token) === -1) {
|
||||
this.push(token);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
while (++i < l);
|
||||
|
||||
if (updated) {
|
||||
this._updateClassName();
|
||||
}
|
||||
};
|
||||
classListProto.remove = function () {
|
||||
var
|
||||
tokens = arguments,
|
||||
i = 0,
|
||||
l = tokens.length,
|
||||
token,
|
||||
updated = false;
|
||||
do {
|
||||
token = tokens[i] + "";
|
||||
var index = checkTokenAndGetIndex(this, token);
|
||||
if (index !== -1) {
|
||||
this.splice(index, 1);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
while (++i < l);
|
||||
|
||||
if (updated) {
|
||||
this._updateClassName();
|
||||
}
|
||||
};
|
||||
classListProto.toggle = function (token, force) {
|
||||
token += "";
|
||||
|
||||
var
|
||||
result = this.contains(token),
|
||||
method = result ? force !== true && "remove" : force !== false && "add";
|
||||
|
||||
if (method) {
|
||||
this[method](token);
|
||||
}
|
||||
|
||||
return !result;
|
||||
};
|
||||
classListProto.toString = function () {
|
||||
return this.join(" ");
|
||||
};
|
||||
|
||||
if (objCtr.defineProperty) {
|
||||
var classListPropDesc = {
|
||||
get: classListGetter,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
};
|
||||
try {
|
||||
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
||||
} catch (ex) { // IE 8 doesn't support enumerable:true
|
||||
if (ex.number === -0x7FF5EC54) {
|
||||
classListPropDesc.enumerable = false;
|
||||
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
||||
}
|
||||
}
|
||||
} else if (objCtr[protoProp].__defineGetter__) {
|
||||
elemCtrProto.__defineGetter__(classListProp, classListGetter);
|
||||
}
|
||||
|
||||
}(self));
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/** sticky-footer v1.0.0, by Chris Ferdinandi | http://github.com/cferdinandi/sticky-footer | Licensed under MIT: http://gomakethings.com/mit/ */
|
||||
"document"in self&&!("classList"in document.createElement("_"))&&!function(t){"use strict";if("Element"in t){var e="classList",n="prototype",i=t.Element[n],r=Object,s=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},a=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},o=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},u=function(t,e){if(""===e)throw new o("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(e))throw new o("INVALID_CHARACTER_ERR","String contains an invalid character");return a.call(t,e)},c=function(t){for(var e=s.call(t.getAttribute("class")||""),n=e?e.split(/\s+/):[],i=0,r=n.length;r>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute("class",this.toString())}},l=c[n]=[],h=function(){return new c(this)};if(o[n]=Error[n],l.item=function(t){return this[t]||null},l.contains=function(t){return t+="",-1!==u(this,t)},l.add=function(){var t,e=arguments,n=0,i=e.length,r=!1;do t=e[n]+"",-1===u(this,t)&&(this.push(t),r=!0);while(++n<i);r&&this._updateClassName()},l.remove=function(){var t,e=arguments,n=0,i=e.length,r=!1;do{t=e[n]+"";var s=u(this,t);-1!==s&&(this.splice(s,1),r=!0)}while(++n<i);r&&this._updateClassName()},l.toggle=function(t,e){t+="";var n=this.contains(t),i=n?e!==!0&&"remove":e!==!1&&"add";return i&&this[i](t),!n},l.toString=function(){return this.join(" ")},r.defineProperty){var f={get:h,enumerable:!0,configurable:!0};try{r.defineProperty(i,e,f)}catch(d){-2146823252===d.number&&(f.enumerable=!1,r.defineProperty(i,e,f))}}else r[n].__defineGetter__&&i.__defineGetter__(e,h)}}(self);
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* sticky-footer v1.0.0
|
||||
* Responsive sticky footers, by Chris Ferdinandi.
|
||||
* http://github.com/cferdinandi/sticky-footer
|
||||
*
|
||||
* Free to use under the MIT License.
|
||||
* http://gomakethings.com/mit/
|
||||
*/
|
||||
|
||||
(function (root, factory) {
|
||||
if ( typeof define === 'function' && define.amd ) {
|
||||
define('stickyFooter', factory(root));
|
||||
} else if ( typeof exports === 'object' ) {
|
||||
module.exports = factory(root);
|
||||
} else {
|
||||
root.stickyFooter = factory(root);
|
||||
}
|
||||
})(this, function (root) {
|
||||
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// Variables
|
||||
//
|
||||
|
||||
var stickyFooter = {}; // Object for public APIs
|
||||
var supports = !!document.querySelector && !!root.addEventListener; // Feature test
|
||||
var settings, wrap, footer, eventTimeout;
|
||||
|
||||
// Default settings
|
||||
var defaults = {
|
||||
callbacks: {
|
||||
before: function () {},
|
||||
after: function () {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* A simple forEach() implementation for Arrays, Objects and NodeLists
|
||||
* @private
|
||||
* @param {Array|Object|NodeList} collection Collection of items to iterate
|
||||
* @param {Function} callback Callback function for each iteration
|
||||
* @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
|
||||
*/
|
||||
var forEach = function (collection, callback, scope) {
|
||||
if (Object.prototype.toString.call(collection) === '[object Object]') {
|
||||
for (var prop in collection) {
|
||||
if (Object.prototype.hasOwnProperty.call(collection, prop)) {
|
||||
callback.call(scope, collection[prop], prop, collection);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var i = 0, len = collection.length; i < len; i++) {
|
||||
callback.call(scope, collection[i], i, collection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Merge defaults with user options
|
||||
* @private
|
||||
* @param {Object} defaults Default settings
|
||||
* @param {Object} options User options
|
||||
* @returns {Object} Merged values of defaults and options
|
||||
*/
|
||||
var extend = function ( defaults, options ) {
|
||||
var extended = {};
|
||||
forEach(defaults, function (value, prop) {
|
||||
extended[prop] = defaults[prop];
|
||||
});
|
||||
forEach(options, function (value, prop) {
|
||||
extended[prop] = options[prop];
|
||||
});
|
||||
return extended;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get height of the viewport
|
||||
* @private
|
||||
* @return {Number} Height of the viewport in pixels
|
||||
*/
|
||||
var getViewportHeight = function () {
|
||||
return Math.max( document.documentElement.clientHeight, window.innerHeight || 0 );
|
||||
};
|
||||
|
||||
/**
|
||||
* Set page wrapper height to fill viewport (minus footer height)
|
||||
* @private
|
||||
* @param {Element} wrap Page wrapper
|
||||
* @param {Element} footer Page footer
|
||||
* @param {Object} settings
|
||||
*/
|
||||
var setWrapHeight = function ( wrap, footer, settings ) {
|
||||
settings.callbacks.before(); // Run callbacks before...
|
||||
wrap.style.minHeight = ( getViewportHeight() - footer.offsetHeight ) + 'px';
|
||||
settings.callbacks.after(); // Run callbacks after...
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the current initialization.
|
||||
* @public
|
||||
*/
|
||||
stickyFooter.destroy = function () {
|
||||
if ( !settings ) return;
|
||||
window.removeEventListener( 'resize', eventThrottler, false );
|
||||
settings = null;
|
||||
wrap = null;
|
||||
footer = null;
|
||||
eventTimeout = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* On window scroll and resize, only run events at a rate of 15fps for better performance
|
||||
* @private
|
||||
* @param {Function} eventTimeout Timeout function
|
||||
* @param {NodeList} wrap The content wrapper for the page
|
||||
* @param {NodeList} footer The footer for the page
|
||||
* @param {Object} settings
|
||||
*/
|
||||
var eventThrottler = function ( eventTimeout, wrap, footer, settings ) {
|
||||
if ( !eventTimeout ) {
|
||||
eventTimeout = setTimeout(function() {
|
||||
eventTimeout = null;
|
||||
setWrapHeight( wrap, footer, settings );
|
||||
}, 66);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize Plugin
|
||||
* @public
|
||||
* @param {Object} options User settings
|
||||
*/
|
||||
stickyFooter.init = function ( options ) {
|
||||
|
||||
// feature test
|
||||
if ( !supports ) return;
|
||||
|
||||
// Destroy any existing initializations
|
||||
stickyFooter.destroy();
|
||||
|
||||
// Selectors and variables
|
||||
settings = extend( defaults, options || {} ); // Merge user options with defaults
|
||||
wrap = document.querySelector( '[data-sticky-wrap]' );
|
||||
footer = document.querySelector( '[data-sticky-footer]' );
|
||||
|
||||
// Stick footer
|
||||
document.documentElement.style.height = '100%';
|
||||
document.body.style.height = '100%';
|
||||
setWrapHeight( wrap, footer, settings );
|
||||
window.addEventListener( 'resize', eventThrottler.bind( null, eventTimeout, wrap, footer, options ), false); // Run Sticky Footer on window resize
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Public APIs
|
||||
//
|
||||
|
||||
return stickyFooter;
|
||||
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
/** sticky-footer v1.0.0, by Chris Ferdinandi | http://github.com/cferdinandi/sticky-footer | Licensed under MIT: http://gomakethings.com/mit/ */
|
||||
!function(e,t){"function"==typeof define&&define.amd?define("stickyFooter",t(e)):"object"==typeof exports?module.exports=t(e):e.stickyFooter=t(e)}(this,function(e){"use strict";var t,n,o,i,c={},r=!!document.querySelector&&!!e.addEventListener,l={callbacks:{before:function(){},after:function(){}}},u=function(e,t,n){if("[object Object]"===Object.prototype.toString.call(e))for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&t.call(n,e[o],o,e);else for(var i=0,c=e.length;c>i;i++)t.call(n,e[i],i,e)},f=function(e,t){var n={};return u(e,function(t,o){n[o]=e[o]}),u(t,function(e,o){n[o]=t[o]}),n},a=function(){return Math.max(document.documentElement.clientHeight,window.innerHeight||0)},d=function(e,t,n){n.callbacks.before(),e.style.minHeight=a()-t.offsetHeight+"px",n.callbacks.after()};c.destroy=function(){t&&(window.removeEventListener("resize",s,!1),t=null,n=null,o=null,i=null)};var s=function(e,t,n,o){e||(e=setTimeout(function(){e=null,d(t,n,o)},66))};return c.init=function(e){r&&(c.destroy(),t=f(l,e||{}),n=document.querySelector("[data-sticky-wrap]"),o=document.querySelector("[data-sticky-footer]"),document.documentElement.style.height="100%",document.body.style.height="100%",d(n,o,t),window.addEventListener("resize",s.bind(null,i,n,o,e),!1))},c});
|
|
@ -0,0 +1,131 @@
|
|||
var gulp = require('gulp');
|
||||
var plumber = require('gulp-plumber');
|
||||
var clean = require('gulp-clean');
|
||||
var rename = require('gulp-rename');
|
||||
var flatten = require('gulp-flatten');
|
||||
var tap = require('gulp-tap');
|
||||
var header = require('gulp-header');
|
||||
var jshint = require('gulp-jshint');
|
||||
var stylish = require('jshint-stylish');
|
||||
var concat = require('gulp-concat');
|
||||
var uglify = require('gulp-uglify');
|
||||
var sass = require('gulp-ruby-sass');
|
||||
var prefix = require('gulp-autoprefixer');
|
||||
var minify = require('gulp-minify-css');
|
||||
var karma = require('gulp-karma');
|
||||
var package = require('./package.json');
|
||||
|
||||
var paths = {
|
||||
output : 'dist/',
|
||||
scripts : {
|
||||
input : [ 'src/js/*' ],
|
||||
output : 'dist/js/'
|
||||
},
|
||||
styles : {
|
||||
input : 'src/sass/**/*.scss',
|
||||
output : 'dist/css/'
|
||||
},
|
||||
static : 'src/static/**',
|
||||
test : {
|
||||
input : [ 'src/js/**/*.js' ],
|
||||
spec : [ 'test/spec/**/*.js' ],
|
||||
coverage: 'test/coverage/',
|
||||
results: 'test/results/'
|
||||
}
|
||||
};
|
||||
|
||||
var banner = {
|
||||
full :
|
||||
'/**\n' +
|
||||
' * <%= package.name %> v<%= package.version %>\n' +
|
||||
' * <%= package.description %>, by <%= package.author.name %>.\n' +
|
||||
' * <%= package.repository.url %>\n' +
|
||||
' * \n' +
|
||||
' * Free to use under the MIT License.\n' +
|
||||
' * http://gomakethings.com/mit/\n' +
|
||||
' */\n\n',
|
||||
min :
|
||||
'/**' +
|
||||
' <%= package.name %> v<%= package.version %>, by Chris Ferdinandi' +
|
||||
' | <%= package.repository.url %>' +
|
||||
' | Licensed under MIT: http://gomakethings.com/mit/' +
|
||||
' */\n'
|
||||
};
|
||||
|
||||
gulp.task('scripts', ['clean'], function() {
|
||||
return gulp.src(paths.scripts.input)
|
||||
.pipe(plumber())
|
||||
.pipe(flatten())
|
||||
.pipe(tap(function (file, t) {
|
||||
if ( file.stat.isDirectory() ) {
|
||||
var name = file.relative + '.js';
|
||||
return gulp.src(file.path + '/*.js')
|
||||
.pipe(concat(name))
|
||||
.pipe(header(banner.full, { package : package }))
|
||||
.pipe(gulp.dest(paths.scripts.output))
|
||||
.pipe(rename({ suffix: '.min' }))
|
||||
.pipe(uglify())
|
||||
.pipe(header(banner.min, { package : package }))
|
||||
.pipe(gulp.dest(paths.scripts.output));
|
||||
}
|
||||
}))
|
||||
.pipe(header(banner.full, { package : package }))
|
||||
.pipe(gulp.dest(paths.scripts.output))
|
||||
.pipe(rename({ suffix: '.min' }))
|
||||
.pipe(uglify())
|
||||
.pipe(header(banner.min, { package : package }))
|
||||
.pipe(gulp.dest(paths.scripts.output));
|
||||
});
|
||||
|
||||
gulp.task('styles', ['clean'], function() {
|
||||
return gulp.src(paths.styles.input)
|
||||
.pipe(plumber())
|
||||
.pipe(sass({style: 'expanded', noCache: true}))
|
||||
.pipe(flatten())
|
||||
.pipe(prefix('last 2 version', '> 1%'))
|
||||
.pipe(header(banner.full, { package : package }))
|
||||
.pipe(gulp.dest(paths.styles.output))
|
||||
.pipe(rename({ suffix: '.min' }))
|
||||
.pipe(minify())
|
||||
.pipe(header(banner.min, { package : package }))
|
||||
.pipe(gulp.dest(paths.styles.output));
|
||||
});
|
||||
|
||||
gulp.task('static', ['clean'], function() {
|
||||
return gulp.src(paths.static)
|
||||
.pipe(plumber())
|
||||
.pipe(gulp.dest(paths.output));
|
||||
});
|
||||
|
||||
gulp.task('lint', function () {
|
||||
return gulp.src(paths.scripts.input)
|
||||
.pipe(plumber())
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('jshint-stylish'));
|
||||
});
|
||||
|
||||
gulp.task('clean', function () {
|
||||
return gulp.src([
|
||||
paths.output,
|
||||
paths.test.coverage,
|
||||
paths.test.results
|
||||
], { read: false })
|
||||
.pipe(plumber())
|
||||
.pipe(clean());
|
||||
});
|
||||
|
||||
gulp.task('test', function() {
|
||||
return gulp.src(paths.test.input.concat(paths.test.spec))
|
||||
.pipe(plumber())
|
||||
.pipe(karma({ configFile: 'test/karma.conf.js' }))
|
||||
.on('error', function(err) { throw err; });
|
||||
});
|
||||
|
||||
gulp.task('default', [
|
||||
'lint',
|
||||
'clean',
|
||||
'scripts',
|
||||
'styles',
|
||||
'static',
|
||||
'test'
|
||||
]);
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MyPlugin</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- A basic responsive reset -->
|
||||
<style>
|
||||
@-webkit-viewport { width: device-width; zoom: 1.0; }
|
||||
@-moz-viewport { width: device-width; zoom: 1.0; }
|
||||
@-ms-viewport { width: device-width; zoom: 1.0; }
|
||||
@-o-viewport { width: device-width; zoom: 1.0; }
|
||||
@viewport { width: device-width; zoom: 1.0; }
|
||||
|
||||
html { overflow-y: auto; }
|
||||
|
||||
img, audio, video, canvas { max-width: 100%; }
|
||||
|
||||
/* Sets body width */
|
||||
.container {
|
||||
max-width: 40em;
|
||||
width: 88%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- HTML5 Shim for IE -->
|
||||
<!--[if IE]>
|
||||
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section class="container" data-sticky-wrap>
|
||||
|
||||
<h1 style="text-align: center; font-size: 3em; margin-bottom: 0;">MyPlugin</h1>
|
||||
<p style="text-align: center; font-size: 1.5em; margin: 0;">description</p>
|
||||
<p style="text-align: center;"><a href="https://github.com/yourname/myplugin">MyPlugin on GitHub</a></p>
|
||||
|
||||
<p>Some short body content.</p>
|
||||
|
||||
</section>
|
||||
|
||||
<footer class="container" data-sticky-footer>
|
||||
<hr>
|
||||
<p>Footer content</p>
|
||||
</footer>
|
||||
|
||||
|
||||
<!-- Javascript -->
|
||||
<script src="dist/js/classList.js"></script>
|
||||
<script src="dist/js/bind-polyfill.js"></script>
|
||||
<script src="dist/js/sticky-footer.js"></script>
|
||||
<script>
|
||||
stickyFooter.init();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "sticky-footer",
|
||||
"version": "1.0.0",
|
||||
"description": "Responsive sticky footers",
|
||||
"author": {
|
||||
"name": "Chris Ferdinandi",
|
||||
"url": "http://gomakethings.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/cferdinandi/sticky-footer"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp": "~3.8.0",
|
||||
"gulp-autoprefixer": "0.0.7",
|
||||
"gulp-clean": "^0.2.4",
|
||||
"gulp-concat": "~2.2.0",
|
||||
"gulp-flatten": "~0.0.2",
|
||||
"gulp-tap": "~0.1.1",
|
||||
"gulp-header": "^1.0.2",
|
||||
"gulp-jshint": "^1.6.1",
|
||||
"gulp-karma": "0.0.4",
|
||||
"gulp-minify-css": "~0.3.4",
|
||||
"gulp-plumber": "~0.6.2",
|
||||
"gulp-rename": "~1.1.0",
|
||||
"gulp-ruby-sass": "~0.7.1",
|
||||
"gulp-uglify": "~0.3.0",
|
||||
"jshint-stylish": "^0.2.0",
|
||||
"karma": "^0.12.16",
|
||||
"karma-coverage": "^0.2.4",
|
||||
"karma-jasmine": "~0.2.0",
|
||||
"karma-phantomjs-launcher": "^0.1.4",
|
||||
"karma-spec-reporter": "0.0.13",
|
||||
"karma-htmlfile-reporter": "~0.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Polyfill Function.prototype.bind support for otherwise ECMA Script 5 compliant browsers
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
|
||||
*/
|
||||
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1);
|
||||
var fToBind = this;
|
||||
fNOP = function () {};
|
||||
fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* classList.js: Cross-browser full element.classList implementation.
|
||||
* 2014-01-31
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
* Public Domain.
|
||||
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
*/
|
||||
|
||||
/*global self, document, DOMException */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
|
||||
|
||||
if ("document" in self && !("classList" in document.createElement("_"))) {
|
||||
|
||||
(function (view) {
|
||||
|
||||
"use strict";
|
||||
|
||||
if (!('Element' in view)) return;
|
||||
|
||||
var
|
||||
classListProp = "classList",
|
||||
protoProp = "prototype",
|
||||
elemCtrProto = view.Element[protoProp],
|
||||
objCtr = Object,
|
||||
strTrim = String[protoProp].trim || function () {
|
||||
return this.replace(/^\s+|\s+$/g, "");
|
||||
},
|
||||
arrIndexOf = Array[protoProp].indexOf || function (item) {
|
||||
var
|
||||
i = 0,
|
||||
len = this.length;
|
||||
for (; i < len; i++) {
|
||||
if (i in this && this[i] === item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
// Vendors: please allow content code to instantiate DOMExceptions
|
||||
DOMEx = function (type, message) {
|
||||
this.name = type;
|
||||
this.code = DOMException[type];
|
||||
this.message = message;
|
||||
},
|
||||
checkTokenAndGetIndex = function (classList, token) {
|
||||
if (token === "") {
|
||||
throw new DOMEx(
|
||||
"SYNTAX_ERR",
|
||||
"An invalid or illegal string was specified"
|
||||
);
|
||||
}
|
||||
if (/\s/.test(token)) {
|
||||
throw new DOMEx(
|
||||
"INVALID_CHARACTER_ERR",
|
||||
"String contains an invalid character"
|
||||
);
|
||||
}
|
||||
return arrIndexOf.call(classList, token);
|
||||
},
|
||||
ClassList = function (elem) {
|
||||
var
|
||||
trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
|
||||
classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
|
||||
i = 0,
|
||||
len = classes.length;
|
||||
for (; i < len; i++) {
|
||||
this.push(classes[i]);
|
||||
}
|
||||
this._updateClassName = function () {
|
||||
elem.setAttribute("class", this.toString());
|
||||
};
|
||||
},
|
||||
classListProto = ClassList[protoProp] = [],
|
||||
classListGetter = function () {
|
||||
return new ClassList(this);
|
||||
};
|
||||
// Most DOMException implementations don't allow calling DOMException's toString()
|
||||
// on non-DOMExceptions. Error's toString() is sufficient here.
|
||||
DOMEx[protoProp] = Error[protoProp];
|
||||
classListProto.item = function (i) {
|
||||
return this[i] || null;
|
||||
};
|
||||
classListProto.contains = function (token) {
|
||||
token += "";
|
||||
return checkTokenAndGetIndex(this, token) !== -1;
|
||||
};
|
||||
classListProto.add = function () {
|
||||
var
|
||||
tokens = arguments,
|
||||
i = 0,
|
||||
l = tokens.length,
|
||||
token,
|
||||
updated = false;
|
||||
do {
|
||||
token = tokens[i] + "";
|
||||
if (checkTokenAndGetIndex(this, token) === -1) {
|
||||
this.push(token);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
while (++i < l);
|
||||
|
||||
if (updated) {
|
||||
this._updateClassName();
|
||||
}
|
||||
};
|
||||
classListProto.remove = function () {
|
||||
var
|
||||
tokens = arguments,
|
||||
i = 0,
|
||||
l = tokens.length,
|
||||
token,
|
||||
updated = false;
|
||||
do {
|
||||
token = tokens[i] + "";
|
||||
var index = checkTokenAndGetIndex(this, token);
|
||||
if (index !== -1) {
|
||||
this.splice(index, 1);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
while (++i < l);
|
||||
|
||||
if (updated) {
|
||||
this._updateClassName();
|
||||
}
|
||||
};
|
||||
classListProto.toggle = function (token, force) {
|
||||
token += "";
|
||||
|
||||
var
|
||||
result = this.contains(token),
|
||||
method = result ? force !== true && "remove" : force !== false && "add";
|
||||
|
||||
if (method) {
|
||||
this[method](token);
|
||||
}
|
||||
|
||||
return !result;
|
||||
};
|
||||
classListProto.toString = function () {
|
||||
return this.join(" ");
|
||||
};
|
||||
|
||||
if (objCtr.defineProperty) {
|
||||
var classListPropDesc = {
|
||||
get: classListGetter,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
};
|
||||
try {
|
||||
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
||||
} catch (ex) { // IE 8 doesn't support enumerable:true
|
||||
if (ex.number === -0x7FF5EC54) {
|
||||
classListPropDesc.enumerable = false;
|
||||
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
||||
}
|
||||
}
|
||||
} else if (objCtr[protoProp].__defineGetter__) {
|
||||
elemCtrProto.__defineGetter__(classListProp, classListGetter);
|
||||
}
|
||||
|
||||
}(self));
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
(function (root, factory) {
|
||||
if ( typeof define === 'function' && define.amd ) {
|
||||
define('stickyFooter', factory(root));
|
||||
} else if ( typeof exports === 'object' ) {
|
||||
module.exports = factory(root);
|
||||
} else {
|
||||
root.stickyFooter = factory(root);
|
||||
}
|
||||
})(this, function (root) {
|
||||
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// Variables
|
||||
//
|
||||
|
||||
var stickyFooter = {}; // Object for public APIs
|
||||
var supports = !!document.querySelector && !!root.addEventListener; // Feature test
|
||||
var settings, wrap, footer, eventTimeout;
|
||||
|
||||
// Default settings
|
||||
var defaults = {
|
||||
callbacks: {
|
||||
before: function () {},
|
||||
after: function () {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* A simple forEach() implementation for Arrays, Objects and NodeLists
|
||||
* @private
|
||||
* @param {Array|Object|NodeList} collection Collection of items to iterate
|
||||
* @param {Function} callback Callback function for each iteration
|
||||
* @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
|
||||
*/
|
||||
var forEach = function (collection, callback, scope) {
|
||||
if (Object.prototype.toString.call(collection) === '[object Object]') {
|
||||
for (var prop in collection) {
|
||||
if (Object.prototype.hasOwnProperty.call(collection, prop)) {
|
||||
callback.call(scope, collection[prop], prop, collection);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var i = 0, len = collection.length; i < len; i++) {
|
||||
callback.call(scope, collection[i], i, collection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Merge defaults with user options
|
||||
* @private
|
||||
* @param {Object} defaults Default settings
|
||||
* @param {Object} options User options
|
||||
* @returns {Object} Merged values of defaults and options
|
||||
*/
|
||||
var extend = function ( defaults, options ) {
|
||||
var extended = {};
|
||||
forEach(defaults, function (value, prop) {
|
||||
extended[prop] = defaults[prop];
|
||||
});
|
||||
forEach(options, function (value, prop) {
|
||||
extended[prop] = options[prop];
|
||||
});
|
||||
return extended;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get height of the viewport
|
||||
* @private
|
||||
* @return {Number} Height of the viewport in pixels
|
||||
*/
|
||||
var getViewportHeight = function () {
|
||||
return Math.max( document.documentElement.clientHeight, window.innerHeight || 0 );
|
||||
};
|
||||
|
||||
/**
|
||||
* Set page wrapper height to fill viewport (minus footer height)
|
||||
* @private
|
||||
* @param {Element} wrap Page wrapper
|
||||
* @param {Element} footer Page footer
|
||||
* @param {Object} settings
|
||||
*/
|
||||
var setWrapHeight = function ( wrap, footer, settings ) {
|
||||
settings.callbacks.before(); // Run callbacks before...
|
||||
wrap.style.minHeight = ( getViewportHeight() - footer.offsetHeight ) + 'px';
|
||||
settings.callbacks.after(); // Run callbacks after...
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the current initialization.
|
||||
* @public
|
||||
*/
|
||||
stickyFooter.destroy = function () {
|
||||
if ( !settings ) return;
|
||||
window.removeEventListener( 'resize', eventThrottler, false );
|
||||
settings = null;
|
||||
wrap = null;
|
||||
footer = null;
|
||||
eventTimeout = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* On window scroll and resize, only run events at a rate of 15fps for better performance
|
||||
* @private
|
||||
* @param {Function} eventTimeout Timeout function
|
||||
* @param {NodeList} wrap The content wrapper for the page
|
||||
* @param {NodeList} footer The footer for the page
|
||||
* @param {Object} settings
|
||||
*/
|
||||
var eventThrottler = function ( eventTimeout, wrap, footer, settings ) {
|
||||
if ( !eventTimeout ) {
|
||||
eventTimeout = setTimeout(function() {
|
||||
eventTimeout = null;
|
||||
setWrapHeight( wrap, footer, settings );
|
||||
}, 66);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize Plugin
|
||||
* @public
|
||||
* @param {Object} options User settings
|
||||
*/
|
||||
stickyFooter.init = function ( options ) {
|
||||
|
||||
// feature test
|
||||
if ( !supports ) return;
|
||||
|
||||
// Destroy any existing initializations
|
||||
stickyFooter.destroy();
|
||||
|
||||
// Selectors and variables
|
||||
settings = extend( defaults, options || {} ); // Merge user options with defaults
|
||||
wrap = document.querySelector( '[data-sticky-wrap]' );
|
||||
footer = document.querySelector( '[data-sticky-footer]' );
|
||||
|
||||
// Stick footer
|
||||
document.documentElement.style.height = '100%';
|
||||
document.body.style.height = '100%';
|
||||
setWrapHeight( wrap, footer, settings );
|
||||
window.addEventListener( 'resize', eventThrottler.bind( null, eventTimeout, wrap, footer, options ), false); // Run Sticky Footer on window resize
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Public APIs
|
||||
//
|
||||
|
||||
return stickyFooter;
|
||||
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath : '',
|
||||
autoWatch : true,
|
||||
frameworks: ['jasmine'],
|
||||
browsers : ['PhantomJS'],
|
||||
plugins : [
|
||||
'karma-spec-reporter',
|
||||
'karma-phantomjs-launcher',
|
||||
'karma-jasmine',
|
||||
'karma-coverage',
|
||||
'karma-htmlfile-reporter'
|
||||
],
|
||||
reporters : ['spec', 'coverage', 'html'],
|
||||
preprocessors: {
|
||||
'../src/js/**/*.js': 'coverage'
|
||||
},
|
||||
coverageReporter: {
|
||||
type : 'html',
|
||||
dir : 'coverage/'
|
||||
},
|
||||
htmlReporter: {
|
||||
outputFile: 'results/unit-tests.html'
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,89 @@
|
|||
describe('My module', function () {
|
||||
|
||||
//
|
||||
// Helper Functions
|
||||
//
|
||||
|
||||
/**
|
||||
* Inserts plugin markup into DOM
|
||||
*/
|
||||
var injectElem = function () {
|
||||
var elem =
|
||||
'<div id="jasmine-fixture">' +
|
||||
'<div data-sticky-wrap>Content</div>' +
|
||||
'<div data-sticky-footer>Footer</div>' +
|
||||
'</div>';
|
||||
document.body.innerHTML = elem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers an event
|
||||
* @param {String} type Type of event (ex. 'click')
|
||||
* @param {Element} elem The element that triggered the event
|
||||
* @link http://stackoverflow.com/a/2490876
|
||||
*/
|
||||
var trigger = function (type, elem) {
|
||||
var event; // The custom event that will be created
|
||||
|
||||
if (document.createEvent) {
|
||||
event = document.createEvent('HTMLEvents');
|
||||
event.initEvent(type, true, true);
|
||||
} else {
|
||||
event = document.createEventObject();
|
||||
event.eventType = type;
|
||||
}
|
||||
|
||||
event.eventName = type;
|
||||
|
||||
if (document.createEvent) {
|
||||
elem.dispatchEvent(event);
|
||||
} else {
|
||||
elem.fireEvent("on" + event.eventType, event);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind polyfill for PhantomJS
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
|
||||
*/
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1);
|
||||
var fToBind = this;
|
||||
var fNOP = function () {};
|
||||
var fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Init
|
||||
//
|
||||
|
||||
describe('Should initialize plugin', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
injectElem();
|
||||
stickyFooter.init();
|
||||
});
|
||||
|
||||
it('Document should include the stickyFooter module', function () {
|
||||
expect(!!stickyFooter).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue