Merge pull request #3461 from PeterDaveHello/Ink_v3.0.4

add Ink v3.0.4
This commit is contained in:
Pete Cooper
2014-06-25 19:20:54 +01:00
127 changed files with 90035 additions and 1 deletions
+2036
View File
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+9663
View File
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+889
View File
@@ -0,0 +1,889 @@
/**
* Ink
*
* A set of tools for quick development of web interfaces.
*
* @package Ink
* @author Sapo Ink Team
* @license http://opensource.org/licenses/MIT MIT
* @link http://ink.sapo.pt
* @version 3.0.0
*/
/**
* Ink class names prefix
* Used in all of Inks components root elements
*/
/**
* Grid class names
*/
/**
* Alerts class names
*/
/**
* Badges class names
*/
/**
* Buttons class names
*/
/**
* Forms class names
*/
/**
* Labels class names
*/
/**
* Navigation class names
*/
/**
* Dropdown class names
*/
/**
* Tables class names
*/
/**
* Images class names
*/
/**
* Sets the grids maximum width
* @var $grid-max-width (pixels|ems|percentage)
*/
/*!
* Sets the grids breakpoint
*
* To add a new break point you'll need to add an element to the list.
*
* Here's an example: ( class-name-prefix, minimum-width, maximum-width, gutter-width, no-media-query).
*
* media: media type.
* class-name-prefix: this is the prefix used in tons of inks classes i.e.: large-100, small-40, etc.
* minimum-width: (pixels|null) the minimum width for the breakpoint
* maximum-width: (pixels|null) the maximum width for the breakpoint
* gutter-width: (ems) the width for the gutters (space between grid columns) for the breakpoint
* no-media-query: (true|false) if you need to support IE8 set this to true on a single breakpoint.
* Since IE8 does not support the @media statement leaving one of the breakpoints
* outside a @media query let's you use the grid in IE8.
*
*/
.ink-grid {
width: auto;
max-width: 1440px;
margin: 0 auto;
}
.ink-grid {
padding: 0 1.75em;
}
.column-group > [class*=large-] {
float: left;
clear: none;
}
.large-5 {
width: 5%;
}
.large-10 {
width: 10%;
}
.large-15 {
width: 15%;
}
.large-20 {
width: 20%;
}
.large-25 {
width: 25%;
}
.large-30 {
width: 30%;
}
.large-33 {
width: 33.33%;
}
.large-35 {
width: 35%;
}
.large-40 {
width: 40%;
}
.large-45 {
width: 45%;
}
.large-50 {
width: 50%;
}
.large-55 {
width: 55%;
}
.large-60 {
width: 60%;
}
.large-65 {
width: 65%;
}
.large-66 {
width: 66.66%;
}
.large-70 {
width: 70%;
}
.large-75 {
width: 75%;
}
.large-80 {
width: 80%;
}
.large-85 {
width: 85%;
}
.large-90 {
width: 90%;
}
.large-95 {
width: 95%;
}
.large-100 {
width: 100%;
}
[class*=large-100] {
float: none;
clear: both;
}
.column-group {
*zoom: 1;
}
.column-group:after {
content: "";
display: table;
clear: both;
}
.column-group.gutters {
margin-left: -1.75em;
}
.column-group.gutters > [class*=large-], .column-group.gutters > [class*=all-] {
padding-left: 1.75em;
margin-bottom: 1.75em;
}
.column-group.horizontal-gutters {
margin-left: -1.75em;
}
.column-group.horizontal-gutters > [class*=large-], .column-group.horizontal-gutters > [class*=all-] {
padding-left: 1.75em;
}
.column-group.vertical-gutters > [class*=large-], .column-group.vertical-gutters > [class*=all-] {
margin-bottom: 1.75em;
}
.column-group.double-gutters {
margin-left: -3.5em;
}
.column-group.double-gutters > [class*=large-], .column-group.double-gutters > [class*=all-] {
padding-left: 3.5em;
margin-bottom: 3.5em;
}
.column-group.double-horizontal-gutters {
margin-left: -3.5em;
}
.column-group.double-horizontal-gutters > [class*=large-], .column-group.double-horizontal-gutters > [class*=all-] {
padding-left: 3.5em;
}
.column-group.double-vertical-gutters > [class*=large-], .column-group.double-vertical-gutters > [class*=all-] {
margin-bottom: 3.5em;
}
.column-group.half-gutters {
margin-left: -0.875em;
}
.column-group.half-gutters > [class*=large-], .column-group.half-gutters > [class*=all-] {
padding-left: 0.875em;
margin-bottom: 0.875em;
}
.column-group.half-horizontal-gutters {
margin-left: -0.875em;
}
.column-group.half-horizontal-gutters > [class*=large-], .column-group.half-horizontal-gutters > [class*=all-] {
padding-left: 0.875em;
}
.column-group.half-vertical-gutters > [class*=large-], .column-group.half-vertical-gutters > [class*=all-] {
margin-bottom: 0.875em;
}
.column-group.quarter-gutters {
margin-left: -0.4375em;
}
.column-group.quarter-gutters > [class*=large-], .column-group.quarter-gutters > [class*=all-] {
padding-left: 0.4375em;
margin-bottom: 0.4375em;
}
.column-group.quarter-horizontal-gutters {
margin-left: -0.4375em;
}
.column-group.quarter-horizontal-gutters > [class*=large-], .column-group.quarter-horizontal-gutters > [class*=all-] {
padding-left: 0.4375em;
}
.column-group.quarter-vertical-gutters > [class*=large-], .column-group.quarter-vertical-gutters > [class*=all-] {
margin-bottom: 0.4375em;
}
.column-group.large-gutters {
margin-left: -1.75em;
}
.column-group.large-gutters > [class*=large-], .column-group.large-gutters > [class*=all-] {
padding-left: 1.75em;
margin-bottom: 1.75em;
}
.column-group.large-horizontal-gutters {
margin-left: -1.75em;
}
.column-group.large-horizontal-gutters > [class*=large-], .column-group.large-horizontal-gutters > [class*=all-] {
padding-left: 1.75em;
}
.column-group.large-vertical-gutters > [class*=large-], .column-group.large-vertical-gutters > [class*=all-] {
margin-bottom: 1.75em;
}
.column-group.large-double-gutters {
margin-left: -3.5em;
}
.column-group.large-double-gutters > [class*=large-], .column-group.large-double-gutters > [class*=all-] {
padding-left: 3.5em;
margin-bottom: 3.5em;
}
.column-group.large-double-horizontal-gutters {
margin-left: -3.5em;
}
.column-group.large-double-horizontal-gutters > [class*=large-], .column-group.large-double-horizontal-gutters > [class*=all-] {
padding-left: 3.5em;
}
.column-group.large-double-vertical-gutters > [class*=large-], .column-group.large-double-vertical-gutters > [class*=all-] {
margin-bottom: 3.5em;
}
.column-group.large-half-gutters {
margin-left: -0.875em;
}
.column-group.large-half-gutters > [class*=large-], .column-group.large-half-gutters > [class*=all-] {
padding-left: 0.875em;
margin-bottom: 0.875em;
}
.column-group.large-half-horizontal-gutters {
margin-left: -0.875em;
}
.column-group.large-half-horizontal-gutters > [class*=large-], .column-group.large-half-horizontal-gutters > [class*=all-] {
padding-left: 0.875em;
}
.column-group.large-half-vertical-gutters > [class*=large-], .column-group.large-half-vertical-gutters > [class*=all-] {
margin-bottom: 0.875em;
}
.column-group.large-quarter-gutters {
margin-left: -0.4375em;
}
.column-group.large-quarter-gutters > [class*=large-], .column-group.large-quarter-gutters > [class*=all-] {
padding-left: 0.4375em;
margin-bottom: 0.4375em;
}
.column-group.large-quarter-horizontal-gutters {
margin-left: -0.4375em;
}
.column-group.large-quarter-horizontal-gutters > [class*=large-], .column-group.large-quarter-horizontal-gutters > [class*=all-] {
padding-left: 0.4375em;
}
.column-group.large-quarter-vertical-gutters > [class*=large-], .column-group.large-quarter-vertical-gutters > [class*=all-] {
margin-bottom: 0.4375em;
}
.column-group.large-no-gutters {
margin-left: 0;
}
.column-group.large-no-gutters > [class*=large-], .column-group.large-no-gutters > [class*=all-] {
padding-left: 0;
margin-bottom: 0;
}
.ink-table td[class*=large-], .ink-table th[class*=large-], .ink-table tr[class*=large-], .ink-table thead[class*=large-], .ink-table tbody[class*=large-], .ink-table tfoot[class*=large-] {
float: none;
}
.large-push-left {
float: left;
}
.large-push-center {
float: none;
margin-left: auto;
margin-right: auto;
}
.large-push-right {
float: right;
}
.large-align-left {
text-align: left;
}
.large-align-center {
text-align: center;
}
.large-align-right {
text-align: right;
}
.large-no-margin {
margin: 0 !important;
}
.space {
margin: 1.75em;
}
.large-space {
margin: 1.75em;
}
.padding {
padding: 1.75em;
}
.large-padding {
padding: 1.75em;
}
.double-space {
margin: 3.5em;
}
.large-double-space {
margin: 3.5em;
}
.double-padding {
padding: 3.5em;
}
.large-double-padding {
padding: 3.5em;
}
.half-space {
margin: 0.875em;
}
.large-half-space {
margin: 0.875em;
}
.half-padding {
padding: 0.875em;
}
.large-half-padding {
padding: 0.875em;
}
.quarter-space {
margin: 0.4375em;
}
.large-quarter-space {
margin: 0.4375em;
}
.quarter-padding {
padding: 0.4375em;
}
.large-quarter-padding {
padding: 0.4375em;
}
.vertical-space {
margin-top: 1.75em;
margin-bottom: 1.75em;
}
.large-vertical-space {
margin-top: 1.75em;
margin-bottom: 1.75em;
}
.vertical-padding {
padding-top: 1.75em;
padding-bottom: 1.75em;
}
.large-vertical-padding {
padding-top: 1.75em;
padding-bottom: 1.75em;
}
.double-vertical-space {
margin-top: 3.5em;
margin-bottom: 3.5em;
}
.large-double-vertical-space {
margin-top: 3.5em;
margin-bottom: 3.5em;
}
.double-vertical-padding {
padding-top: 3.5em;
padding-bottom: 3.5em;
}
.large-double-vertical-padding {
padding-top: 3.5em;
padding-bottom: 3.5em;
}
.half-vertical-space {
margin-top: 0.875em;
margin-bottom: 0.875em;
}
.large-half-vertical-space {
margin-top: 0.875em;
margin-bottom: 0.875em;
}
.half-vertical-padding {
padding-top: 0.875em;
padding-bottom: 0.875em;
}
.large-half-vertical-padding {
padding-top: 0.875em;
padding-bottom: 0.875em;
}
.quarter-vertical-space {
margin-top: 0.4375em;
margin-bottom: 0.4375em;
}
.large-quarter-vertical-space {
margin-top: 0.4375em;
margin-bottom: 0.4375em;
}
.quarter-vertical-padding {
padding-top: 0.4375em;
padding-bottom: 0.4375em;
}
.large-quarter-vertical-padding {
padding-top: 0.4375em;
padding-bottom: 0.4375em;
}
.horizontal-space {
margin-left: 1.75em;
margin-right: 1.75em;
}
.large-horizontal-space {
margin-left: 1.75em;
margin-right: 1.75em;
}
.horizontal-padding {
padding-left: 1.75em;
padding-right: 1.75em;
}
.large-horizontal-padding {
padding-left: 1.75em;
padding-right: 1.75em;
}
.double-horizontal-space {
margin-left: 3.5em;
margin-right: 3.5em;
}
.large-double-horizontal-space {
margin-left: 3.5em;
margin-right: 3.5em;
}
.double-horizontal-padding {
padding-left: 3.5em;
padding-right: 3.5em;
}
.large-double-horizontal-padding {
padding-left: 3.5em;
padding-right: 3.5em;
}
.half-horizontal-space {
margin-left: 0.875em;
margin-right: 0.875em;
}
.large-half-horizontal-space {
margin-left: 0.875em;
margin-right: 0.875em;
}
.half-horizontal-padding {
padding-left: 0.875em;
padding-right: 0.875em;
}
.large-half-horizontal-padding {
padding-left: 0.875em;
padding-right: 0.875em;
}
.quarter-horizontal-space {
margin-left: 0.4375em;
margin-right: 0.4375em;
}
.large-quarter-horizontal-space {
margin-left: 0.4375em;
margin-right: 0.4375em;
}
.quarter-horizontal-padding {
padding-left: 0.4375em;
padding-right: 0.4375em;
}
.large-quarter-horizontal-padding {
padding-left: 0.4375em;
padding-right: 0.4375em;
}
.top-space {
margin-top: 1.75em;
}
.large-top-space {
margin-top: 1.75em;
}
.top-padding {
padding-top: 1.75em;
}
.large-top-padding {
padding-top: 1.75em;
}
.double-top-space {
margin-top: 3.5em;
}
.large-double-top-space {
margin-top: 3.5em;
}
.double-top-padding {
padding-top: 3.5em;
}
.large-double-top-padding {
padding-top: 3.5em;
}
.half-top-space {
margin-top: 0.875em;
}
.large-half-top-space {
margin-top: 0.875em;
}
.half-top-padding {
padding-top: 0.875em;
}
.large-half-top-padding {
padding-top: 0.875em;
}
.quarter-top-space {
margin-top: 0.4375em;
}
.large-quarter-top-space {
margin-top: 0.4375em;
}
.quarter-top-padding {
padding-top: 0.4375em;
}
.large-quarter-top-padding {
padding-top: 0.4375em;
}
.right-space {
margin-right: 1.75em;
}
.large-right-space {
margin-right: 1.75em;
}
.right-padding {
padding-right: 1.75em;
}
.large-right-padding {
padding-right: 1.75em;
}
.double-right-space {
margin-right: 3.5em;
}
.large-double-right-space {
margin-right: 3.5em;
}
.double-right-padding {
padding-right: 3.5em;
}
.large-double-right-padding {
padding-right: 3.5em;
}
.half-right-space {
margin-right: 0.875em;
}
.large-half-right-space {
margin-right: 0.875em;
}
.half-right-padding {
padding-right: 0.875em;
}
.large-half-right-padding {
padding-right: 0.875em;
}
.quarter-right-space {
margin-right: 0.4375em;
}
.large-quarter-right-space {
margin-right: 0.4375em;
}
.quarter-right-padding {
padding-right: 0.4375em;
}
.large-quarter-right-padding {
padding-right: 0.4375em;
}
.bottom-space {
margin-bottom: 1.75em;
}
.large-bottom-space {
margin-bottom: 1.75em;
}
.bottom-padding {
padding-bottom: 1.75em;
}
.large-bottom-padding {
padding-bottom: 1.75em;
}
.double-bottom-space {
margin-bottom: 3.5em;
}
.large-double-bottom-space {
margin-bottom: 3.5em;
}
.double-bottom-padding {
padding-bottom: 3.5em;
}
.large-double-bottom-padding {
padding-bottom: 3.5em;
}
.half-bottom-space {
margin-bottom: 0.875em;
}
.large-half-bottom-space {
margin-bottom: 0.875em;
}
.half-bottom-padding {
padding-bottom: 0.875em;
}
.large-half-bottom-padding {
padding-bottom: 0.875em;
}
.quarter-bottom-space {
margin-bottom: 0.4375em;
}
.large-quarter-bottom-space {
margin-bottom: 0.4375em;
}
.quarter-bottom-padding {
padding-bottom: 0.4375em;
}
.large-quarter-bottom-padding {
padding-bottom: 0.4375em;
}
.left-space {
margin-left: 1.75em;
}
.large-left-space {
margin-left: 1.75em;
}
.left-padding {
padding-left: 1.75em;
}
.large-left-padding {
padding-left: 1.75em;
}
.double-left-space {
margin-left: 3.5em;
}
.large-double-left-space {
margin-left: 3.5em;
}
.double-left-padding {
padding-left: 3.5em;
}
.large-double-left-padding {
padding-left: 3.5em;
}
.half-left-space {
margin-left: 0.875em;
}
.large-half-left-space {
margin-left: 0.875em;
}
.half-left-padding {
padding-left: 0.875em;
}
.large-half-left-padding {
padding-left: 0.875em;
}
.quarter-left-space {
margin-left: 0.4375em;
}
.large-quarter-left-space {
margin-left: 0.4375em;
}
.quarter-left-padding {
padding-left: 0.4375em;
}
.large-quarter-left-padding {
padding-left: 0.4375em;
}
.hide-large {
display: none !important;
}
.show-large {
display: inherit !important;
}
table.show-large {
display: table;
}
thead.show-large {
display: table-header-group;
}
tbody.show-large {
display: table-row-group;
}
tfoot.show-large {
display: table-footer-group;
}
tr.show-large {
display: table-row;
}
th.show-large,
td.show-large {
display: table-cell;
}
+1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+8544
View File
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+41
View File
@@ -0,0 +1,41 @@
/**
* Sets the grids maximum width
* @var $grid-max-width (pixels|ems|percentage)
*/
/*!
* Sets the grids breakpoint
*
* To add a new break point you'll need to add an element to the list.
*
* Here's an example: ( class-name-prefix, minimum-width, maximum-width, gutter-width, no-media-query).
*
* media: media type.
* class-name-prefix: this is the prefix used in tons of inks classes i.e.: large-100, small-40, etc.
* minimum-width: (pixels|null) the minimum width for the breakpoint
* maximum-width: (pixels|null) the maximum width for the breakpoint
* gutter-width: (ems) the width for the gutters (space between grid columns) for the breakpoint
* no-media-query: (true|false) if you need to support IE8 set this to true on a single breakpoint.
* Since IE8 does not support the @media statement leaving one of the breakpoints
* outside a @media query let's you use the grid in IE8.
*
*/
/* for screen screens */
@media screen and (min-width: xlarge) and (max-width: 1261px) {
/* your css for screen screens goes here ... */
}
/* for screen screens */
@media screen and (min-width: large) and (max-width: 961px) {
/* your css for screen screens goes here ... */
}
/* for screen screens */
@media screen and (min-width: medium) and (max-width: 641px) {
/* your css for screen screens goes here ... */
}
/* for screen screens */
@media screen and (min-width: small) and (max-width: 321px) {
/* your css for screen screens goes here ... */
}
/* for screen screens */
@media screen and (min-width: tiny) {
/* your css for screen screens goes here ... */
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+154
View File
@@ -0,0 +1,154 @@
/**
* @module Ink.Autoload
* @version 1
* Create Ink UI components easily
*/
Ink.createModule('Ink.Autoload', 1, ['Ink.Dom.Selector_1', 'Ink.Util.Array_1', 'Ink.Dom.Loaded_1', 'Ink.UI.SmoothScroller_1', 'Ink.UI.Close_1'], function( Selector, InkArray, Loaded, Scroller, Close ){
'use strict';
/**
* @namespace Ink.Autoload
* @static
*/
var el = document.createElement('div');
// See if a selector is valid.
function validSelector(sel) {
try {
Selector.select(sel, el);
} catch(e) {
Ink.error(e);
return false;
}
return true;
}
var Autoload = {
/**
* Matches module names to default selectors.
*
* @property selectors {Object}
* @public
**/
selectors: {
/* Match module names to element classes (or more complex selectors)
* which get the UI modules instantiated automatically. */
'Animate_1' : '.ink-animate',
'Carousel_1' : '.ink-carousel',
'DatePicker_1' : '.ink-datepicker',
'Dropdown_1' : '.ink-dropdown',
'Gallery_1' : 'ul.ink-gallery-source',
'Modal_1' : '.ink-modal',
'ProgressBar_1' : '.ink-progress-bar',
'SortableList_1': '.ink-sortable-list',
'Spy_1' : '[data-spy="true"]',
'Stacker_1' : '.ink-stacker',
'Sticky_1' : '.ink-sticky, .sticky',
'Table_1' : '.ink-table',
'Tabs_1' : '.ink-tabs',
'Toggle_1' : '.ink-toggle, .toggle',
'Tooltip_1' : '.ink-tooltip, .tooltip',
'TreeView_1' : '.ink-tree-view'
},
defaultOptions: {},
/**
* Run Autoload on a specific element.
*
* Useful when you load something from AJAX and want it to have automatically loaded Ink modules.
* @method run
* @param {DOMElement} parentEl
* @param {Object} [options] Options object, containing:
* @param {Boolean} [options.forceAutoload] Autoload things on elements even if they have `data-autoload="false"`
* @param {Boolean} [options.createClose] Whether to create the Ink.UI.Close component. Defaults to `true`.
* @param {Boolean} [options.createSmoothScroller] Whether to create the Scroller component. Defaults to `true`.
* @param {Object} [options.selectors=Ink.Autoload.selectors] A hash mapping module names to selectors that match elements to load these modules. For example, `{ 'Modal_1': '.my-specific-modal' }`.
* @param {Boolean} [options.waitForDOMLoaded=false] Do nothing until the DOM is loaded. Uses Ink.Dom.Loaded.run();
* @public
* @sample Autoload_1.html
**/
run: function (parentEl, options){
options = Ink.extendObj({
// The below lines are not required because undefined is falsy anyway..
// forceAutoload: false,
// waitForDOMLoaded: false,
// createClose: false,
// createSmoothScroller: false,
selectors: Autoload.selectors
}, options || {});
for(var mod in options.selectors) if (options.selectors.hasOwnProperty(mod)) {
// `elements` need to be in a closure because requireModules is async.
findElements(mod);
}
if (options.createClose !== false) {
new Close();
}
if (options.createSmoothScroller !== false) {
Scroller.init();
}
function findElements(mod) {
var modName = 'Ink.UI.' + mod;
var elements = Selector.select( options.selectors[mod], parentEl );
elements = InkArray.filter(elements, autoloadElement);
if( elements.length ){
Ink.requireModules( [modName], function( Component ) {
InkArray.forEach(elements, function (el) {
new Component(el, Autoload.defaultOptions[modName]);
});
});
}
}
function autoloadElement(element) {
if (options.forceAutoload === true) { return true; }
if (typeof element.getAttribute === 'function') {
return element.getAttribute('data-autoload') !== 'false';
}
}
},
/**
* Add a new entry to be autoloaded.
* @method add
* @param moduleName {String}
* @param selector {String}
*/
add: function (moduleName, selector) {
if (!validSelector(selector)) { return false; }
if (Autoload.selectors[moduleName]) {
Autoload.selectors[moduleName] += ', ' + selector;
} else {
Autoload.selectors[moduleName] = selector;
}
},
/**
* Removes a module from autoload, making it not be automatically loaded.
* @method remove
* @param moduleName {String}
**/
remove: function (moduleName) {
delete Autoload.selectors[moduleName];
}
};
for (var k in Autoload.selectors) if (Autoload.selectors.hasOwnProperty(k)) {
Autoload.defaultOptions[k] = {};
}
if (!window.INK_NO_AUTO_LOAD) {
Loaded.run(function () {
Autoload.run(document, {
createSmoothScroller: true,
createClose: true
});
Autoload.firstRunDone = true;
});
}
return Autoload;
});
+440
View File
@@ -0,0 +1,440 @@
/*
Holder - 1.9 - client side image placeholders
(c) 2012-2013 Ivan Malopinsky / http://imsky.co
Provided under the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
Commercial use requires attribution.
*/
var Holder = Holder || {};
(function (app, win) {
var preempted = false,
fallback = false,
canvas = document.createElement('canvas');
//getElementsByClassName polyfill
document.getElementsByClassName||(document.getElementsByClassName=function(e){var t=document,n,r,i,s=[];if(t.querySelectorAll)return t.querySelectorAll("."+e);if(t.evaluate){r=".//*[contains(concat(' ', @class, ' '), ' "+e+" ')]",n=t.evaluate(r,t,null,0,null);while(i=n.iterateNext())s.push(i)}else{n=t.getElementsByTagName("*"),r=new RegExp("(^|\\s)"+e+"(\\s|$)");for(i=0;i<n.length;i++)r.test(n[i].className)&&s.push(n[i])}return s})
//getComputedStyle polyfill
window.getComputedStyle||(window.getComputedStyle=function(e,t){return this.el=e,this.getPropertyValue=function(t){var n=/(\-([a-z]){1})/g;return t=="float"&&(t="styleFloat"),n.test(t)&&(t=t.replace(n,function(){return arguments[2].toUpperCase()})),e.currentStyle[t]?e.currentStyle[t]:null},this})
//http://javascript.nwbox.com/ContentLoaded by Diego Perini with modifications
function contentLoaded(n,t){var l="complete",s="readystatechange",u=!1,h=u,c=!0,i=n.document,a=i.documentElement,e=i.addEventListener?"addEventListener":"attachEvent",v=i.addEventListener?"removeEventListener":"detachEvent",f=i.addEventListener?"":"on",r=function(e){(e.type!=s||i.readyState==l)&&((e.type=="load"?n:i)[v](f+e.type,r,u),!h&&(h=!0)&&t.call(n,null))},o=function(){try{a.doScroll("left")}catch(n){setTimeout(o,50);return}r("poll")};if(i.readyState==l)t.call(n,"lazy");else{if(i.createEventObject&&a.doScroll){try{c=!n.frameElement}catch(y){}c&&o()}i[e](f+"DOMContentLoaded",r,u),i[e](f+s,r,u),n[e](f+"load",r,u)}};
//https://gist.github.com/991057 by Jed Schmidt with modifications
function selector(a){
a=a.match(/^(\W)?(.*)/);var b=document["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2]);
var ret=[]; b!=null&&(b.length?ret=b:b.length==0?ret=b:ret=[b]); return ret;
}
//shallow object property extend
function extend(a,b){var c={};for(var d in a)c[d]=a[d];for(var e in b)c[e]=b[e];return c}
//hasOwnProperty polyfill
if (!Object.prototype.hasOwnProperty)
Object.prototype.hasOwnProperty = function(prop) {
var proto = this.__proto__ || this.constructor.prototype;
return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]);
}
function text_size(width, height, template) {
var dimension_arr = [height, width].sort();
var maxFactor = Math.round(dimension_arr[1] / 16),
minFactor = Math.round(dimension_arr[0] / 16);
var text_height = Math.max(template.size, maxFactor);
return {
height: text_height
}
}
function draw(ctx, dimensions, template, ratio) {
var ts = text_size(dimensions.width, dimensions.height, template);
var text_height = ts.height;
var width = dimensions.width * ratio,
height = dimensions.height * ratio;
var font = template.font ? template.font : "sans-serif";
canvas.width = width;
canvas.height = height;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = template.background;
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = template.foreground;
ctx.font = "bold " + text_height + "px " + font;
var text = template.text ? template.text : (dimensions.width + "x" + dimensions.height);
if (ctx.measureText(text).width / width > 1) {
text_height = template.size / (ctx.measureText(text).width / width);
}
//Resetting font size if necessary
ctx.font = "bold " + (text_height * ratio) + "px " + font;
ctx.fillText(text, (width / 2), (height / 2), width);
return canvas.toDataURL("image/png");
}
function render(mode, el, holder, src) {
var dimensions = holder.dimensions,
theme = holder.theme,
text = holder.text ? decodeURIComponent(holder.text) : holder.text;
var dimensions_caption = dimensions.width + "x" + dimensions.height;
theme = (text ? extend(theme, {
text: text
}) : theme);
theme = (holder.font ? extend(theme, {
font: holder.font
}) : theme);
if (mode == "image") {
el.setAttribute("data-src", src);
el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption);
if (fallback || !holder.auto) {
el.style.width = dimensions.width + "px";
el.style.height = dimensions.height + "px";
}
if (fallback) {
el.style.backgroundColor = theme.background;
} else {
el.setAttribute("src", draw(ctx, dimensions, theme, ratio));
}
} else {
if (!fallback) {
el.style.backgroundImage = "url(" + draw(ctx, dimensions, theme, ratio) + ")";
el.style.backgroundSize = dimensions.width + "px " + dimensions.height + "px";
}
}
};
function fluid(el, holder, src) {
var dimensions = holder.dimensions,
theme = holder.theme,
text = holder.text;
var dimensions_caption = dimensions.width + "x" + dimensions.height;
theme = (text ? extend(theme, {
text: text
}) : theme);
var fluid = document.createElement("div");
if (el.fluidRef) {
fluid = el.fluidRef;
}
fluid.style.backgroundColor = theme.background;
fluid.style.color = theme.foreground;
fluid.className = el.className + " holderjs-fluid";
fluid.style.width = holder.dimensions.width + (holder.dimensions.width.indexOf("%") > 0 ? "" : "px");
fluid.style.height = holder.dimensions.height + (holder.dimensions.height.indexOf("%") > 0 ? "" : "px");
fluid.id = el.id;
el.style.width = 0;
el.style.height = 0;
if (!el.fluidRef) {
if (theme.text) {
fluid.appendChild(document.createTextNode(theme.text))
} else {
fluid.appendChild(document.createTextNode(dimensions_caption))
fluid_images.push(fluid);
setTimeout(fluid_update, 0);
}
}
el.fluidRef = fluid;
el.parentNode.insertBefore(fluid, el.nextSibling)
if (window.jQuery) {
jQuery(function ($) {
$(el).on("load", function () {
el.style.width = fluid.style.width;
el.style.height = fluid.style.height;
$(el).show();
$(fluid).remove();
});
})
}
}
function fluid_update() {
for (i in fluid_images) {
if (!fluid_images.hasOwnProperty(i)) continue;
var el = fluid_images[i],
label = el.firstChild;
el.style.lineHeight = el.offsetHeight + "px";
label.data = el.offsetWidth + "x" + el.offsetHeight;
}
}
function parse_flags(flags, options) {
var ret = {
theme: settings.themes.gray
}, render = false;
for (sl = flags.length, j = 0; j < sl; j++) {
var flag = flags[j];
if (app.flags.dimensions.match(flag)) {
render = true;
ret.dimensions = app.flags.dimensions.output(flag);
} else if (app.flags.fluid.match(flag)) {
render = true;
ret.dimensions = app.flags.fluid.output(flag);
ret.fluid = true;
} else if (app.flags.colors.match(flag)) {
ret.theme = app.flags.colors.output(flag);
} else if (options.themes[flag]) {
//If a theme is specified, it will override custom colors
ret.theme = options.themes[flag];
} else if (app.flags.text.match(flag)) {
ret.text = app.flags.text.output(flag);
} else if (app.flags.font.match(flag)) {
ret.font = app.flags.font.output(flag);
} else if (app.flags.auto.match(flag)) {
ret.auto = true;
}
}
return render ? ret : false;
};
if (!canvas.getContext) {
fallback = true;
} else {
if (canvas.toDataURL("image/png")
.indexOf("data:image/png") < 0) {
//Android doesn't support data URI
fallback = true;
} else {
var ctx = canvas.getContext("2d");
}
}
var dpr = 1, bsr = 1;
if(!fallback){
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
}
var ratio = dpr / bsr;
var fluid_images = [];
var settings = {
domain: "holder.js",
images: "img",
bgnodes: ".holderjs",
themes: {
"gray": {
background: "#eee",
foreground: "#aaa",
size: 12
},
"social": {
background: "#3a5a97",
foreground: "#fff",
size: 12
},
"industrial": {
background: "#434A52",
foreground: "#C2F200",
size: 12
},
"ink": {
background: "#222",
foreground: "#fff",
size: 18
}
},
stylesheet: ".holderjs-fluid {font-size:16px;font-weight:bold;text-align:center;font-family:sans-serif;margin:0}"
};
app.flags = {
dimensions: {
regex: /^(\d+)x(\d+)$/,
output: function (val) {
var exec = this.regex.exec(val);
return {
width: +exec[1],
height: +exec[2]
}
}
},
fluid: {
regex: /^([0-9%]+)x([0-9%]+)$/,
output: function (val) {
var exec = this.regex.exec(val);
return {
width: exec[1],
height: exec[2]
}
}
},
colors: {
regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i,
output: function (val) {
var exec = this.regex.exec(val);
return {
size: settings.themes.gray.size,
foreground: "#" + exec[2],
background: "#" + exec[1]
}
}
},
text: {
regex: /text\:(.*)/,
output: function (val) {
return this.regex.exec(val)[1];
}
},
font: {
regex: /font\:(.*)/,
output: function (val) {
return this.regex.exec(val)[1];
}
},
auto: {
regex: /^auto$/
}
}
for (var flag in app.flags) {
if (!app.flags.hasOwnProperty(flag)) continue;
app.flags[flag].match = function (val) {
return val.match(this.regex)
}
}
app.add_theme = function (name, theme) {
name != null && theme != null && (settings.themes[name] = theme);
return app;
};
app.add_image = function (src, el) {
var node = selector(el);
if (node.length) {
for (var i = 0, l = node.length; i < l; i++) {
var img = document.createElement("img")
img.setAttribute("data-src", src);
node[i].appendChild(img);
}
}
return app;
};
app.run = function (o) {
var options = extend(settings, o),
images = [], imageNodes = [], bgnodes = [];
if(typeof(options.images) == "string"){
imageNodes = selector(options.images);
}
else if (window.NodeList && options.images instanceof window.NodeList) {
imageNodes = options.images;
} else if (window.Node && options.images instanceof window.Node) {
imageNodes = [options.images];
}
if(typeof(options.bgnodes) == "string"){
bgnodes = selector(options.bgnodes);
} else if (window.NodeList && options.elements instanceof window.NodeList) {
bgnodes = options.bgnodes;
} else if (window.Node && options.bgnodes instanceof window.Node) {
bgnodes = [options.bgnodes];
}
preempted = true;
for (i = 0, l = imageNodes.length; i < l; i++) images.push(imageNodes[i]);
var holdercss = document.getElementById("holderjs-style");
if (!holdercss) {
holdercss = document.createElement("style");
holdercss.setAttribute("id", "holderjs-style");
holdercss.type = "text/css";
document.getElementsByTagName("head")[0].appendChild(holdercss);
}
if (!options.nocss) {
if (holdercss.styleSheet) {
holdercss.styleSheet.cssText += options.stylesheet;
} else {
holdercss.appendChild(document.createTextNode(options.stylesheet));
}
}
var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)");
for (var l = bgnodes.length, i = 0; i < l; i++) {
var src = window.getComputedStyle(bgnodes[i], null)
.getPropertyValue("background-image");
var flags = src.match(cssregex);
if (flags) {
var holder = parse_flags(flags[1].split("/"), options);
if (holder) {
render("background", bgnodes[i], holder, src);
}
}
}
for (l = images.length, i = 0; i < l; i++) {
var attr_src = attr_data_src = src = null;
try{
attr_src = images[i].getAttribute("src");
attr_datasrc = images[i].getAttribute("data-src");
}catch(e){}
if (attr_datasrc == null && !! attr_src && attr_src.indexOf(options.domain) >= 0) {
src = attr_src;
} else if ( !! attr_datasrc && attr_datasrc.indexOf(options.domain) >= 0) {
src = attr_datasrc;
}
if (src) {
var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1)
.split("/"), options);
if (holder) {
if (holder.fluid) {
fluid(images[i], holder, src);
} else {
render("image", images[i], holder, src);
}
}
}
}
return app;
};
contentLoaded(win, function () {
if (window.addEventListener) {
window.addEventListener("resize", fluid_update, false);
window.addEventListener("orientationchange", fluid_update, false);
} else {
window.attachEvent("onresize", fluid_update)
}
preempted || app.run();
});
if (typeof define === "function" && define.amd) {
define("Holder", [], function () {
return app;
});
}
})(Holder, window);
+11
View File
@@ -0,0 +1,11 @@
/*
HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
(function(j,f){function s(a,b){var c=a.createElement("p"),m=a.getElementsByTagName("head")[0]||a.documentElement;c.innerHTML="x<style>"+b+"</style>";return m.insertBefore(c.lastChild,m.firstChild)}function o(){var a=d.elements;return"string"==typeof a?a.split(" "):a}function n(a){var b=t[a[u]];b||(b={},p++,a[u]=p,t[p]=b);return b}function v(a,b,c){b||(b=f);if(e)return b.createElement(a);c||(c=n(b));b=c.cache[a]?c.cache[a].cloneNode():y.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);
return b.canHaveChildren&&!z.test(a)?c.frag.appendChild(b):b}function A(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();a.createElement=function(c){return!d.shivMethods?b.createElem(c):v(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+o().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(d,b.frag)}
function w(a){a||(a=f);var b=n(a);if(d.shivCSS&&!q&&!b.hasCSS)b.hasCSS=!!s(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}");e||A(a,b);return a}function B(a){for(var b,c=a.attributes,m=c.length,f=a.ownerDocument.createElement(l+":"+a.nodeName);m--;)b=c[m],b.specified&&f.setAttribute(b.nodeName,b.nodeValue);f.style.cssText=a.style.cssText;return f}function x(a){function b(){clearTimeout(d._removeSheetTimer);
c&&c.removeNode(!0);c=null}var c,f,d=n(a),e=a.namespaces,j=a.parentWindow;if(!C||a.printShived)return a;"undefined"==typeof e[l]&&e.add(l);j.attachEvent("onbeforeprint",function(){b();var g,i,d;d=a.styleSheets;for(var e=[],h=d.length,k=Array(h);h--;)k[h]=d[h];for(;d=k.pop();)if(!d.disabled&&D.test(d.media)){try{g=d.imports,i=g.length}catch(j){i=0}for(h=0;h<i;h++)k.push(g[h]);try{e.push(d.cssText)}catch(n){}}g=e.reverse().join("").split("{");i=g.length;h=RegExp("(^|[\\s,>+~])("+o().join("|")+")(?=[[\\s,>+~#.:]|$)",
"gi");for(k="$1"+l+"\\:$2";i--;)e=g[i]=g[i].split("}"),e[e.length-1]=e[e.length-1].replace(h,k),g[i]=e.join("}");e=g.join("{");i=a.getElementsByTagName("*");h=i.length;k=RegExp("^(?:"+o().join("|")+")$","i");for(d=[];h--;)g=i[h],k.test(g.nodeName)&&d.push(g.applyElement(B(g)));f=d;c=s(a,e)});j.attachEvent("onafterprint",function(){for(var a=f,c=a.length;c--;)a[c].removeNode();clearTimeout(d._removeSheetTimer);d._removeSheetTimer=setTimeout(b,500)});a.printShived=!0;return a}var r=j.html5||{},z=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,
y=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q,u="_html5shiv",p=0,t={},e;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";q="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}e=b}catch(d){e=q=!0}})();var d={elements:r.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",
version:"3.7.0",shivCSS:!1!==r.shivCSS,supportsUnknownElements:e,shivMethods:!1!==r.shivMethods,type:"default",shivDocument:w,createElement:v,createDocumentFragment:function(a,b){a||(a=f);if(e)return a.createDocumentFragment();for(var b=b||n(a),c=b.frag.cloneNode(),d=0,j=o(),l=j.length;d<l;d++)c.createElement(j[d]);return c}};j.html5=d;w(f);var D=/^$|\b(?:all|print)\b/,l="html5shiv",C=!e&&function(){var a=f.documentElement;return!("undefined"==typeof f.namespaces||"undefined"==typeof f.parentWindow||
"undefined"==typeof a.applyElement||"undefined"==typeof a.removeNode||"undefined"==typeof j.attachEvent)}();d.type+=" print";d.shivPrint=x;x(f)})(this,document);
+8
View File
@@ -0,0 +1,8 @@
/*
HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}</style>";
c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);
if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);
+25051
View File
File diff suppressed because it is too large Load Diff
+10
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+12014
View File
File diff suppressed because it is too large Load Diff
+6
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+167
View File
@@ -0,0 +1,167 @@
/**
* Animate.css Utility
*
* This module is a wrapper around animate.css's CSS classes to produce animation.
* It contains options to ease common tasks, like listen to the "animationend" event with all necessary prefixes, remove the necessary class names when the animation finishes, or configure the duration of your animation with the necessary browser prefix.
*
* @module Ink.UI.Animate_1
* @version 1
*/
Ink.createModule('Ink.UI.Animate', 1, ['Ink.UI.Common_1', 'Ink.Dom.Event_1', 'Ink.Dom.Css_1'], function (Common, InkEvent, Css) {
'use strict';
var animationPrefix = (function (el) {
return ('animationName' in el.style) ? 'animation' :
('oAnimationName' in el.style) ? 'oAnimation' :
('msAnimationName' in el.style) ? 'msAnimation' :
('webkitAnimationName' in el.style) ? 'webkitAnimation' : null;
}(document.createElement('div')));
var animationEndEventName = {
animation: 'animationend',
oAnimation: 'oanimationend',
msAnimation: 'MSAnimationEnd',
webkitAnimation: 'webkitAnimationEnd'
}[animationPrefix];
/**
* @class Ink.UI.Animate_1
* @constructor
*
* @param {DOMElement} element Animated element
* @param {Object} options Options object
* @param {String} options.animation Animation name
* @param {String|Number} [options.duration] Duration name (fast|medium|slow) or duration in milliseconds. Defaults to 'medium'.
* @param {Boolean} [options.removeClass] Flag to remove the CSS class when finished animating. Defaults to false.
* @param {Function} [options.onEnd] Callback for the animation end
*
* @sample Ink_UI_Animate_1.html
*
**/
function Animate() {
Common.BaseUIComponent.apply(this, arguments);
}
Animate._name = 'Animate_1';
Animate._optionDefinition = {
trigger: ['Element', null],
duration: ['String', 'slow'], // Actually a string with a duration name, or a number of ms
animation: ['String'],
removeClass: ['Boolean', true],
onEnd: ['Function', function () {}]
};
Animate.prototype._init = function () {
if (!isNaN(parseInt(this._options.duration, 10))) {
this._options.duration = parseInt(this._options.duration, 10);
}
if (this._options.trigger) {
InkEvent.observe(this._options.trigger, 'click', Ink.bind(function () {
this.animate();
}, this)); // later
} else {
this.animate();
}
};
Animate.prototype.animate = function () {
Animate.animate(this._element, this._options.animation, this._options);
};
Ink.extendObj(Animate, {
/**
* Browser prefix for the CSS animations.
*
* @property _animationPrefix
* @private
**/
_animationPrefix: animationPrefix,
/**
* Boolean which says whether this browser has CSS3 animation support.
*
* @property animationSupported
**/
animationSupported: !!animationPrefix,
/**
* Prefixed 'animationend' event name.
*
* @property animationEndEventName
**/
animationEndEventName: animationEndEventName,
/**
* Animate an element using one of the animate.css classes
*
* **Note: This is a utility method inside the `Animate` class, which you can access through `Animate.animate()`. Do not mix these up.**
*
* @static
* @method animate
* @param element {DOMElement} animated element
* @param animation {String} animation name
* @param [options] {Object}
* @param [options.onEnd=null] {Function} callback for animation end
* @param [options.removeClass=false] {Boolean} whether to remove the Css class when finished
* @param [options.duration=medium] {String|Number} duration name (fast|medium|slow) or duration in ms
*
* @sample Ink_UI_Animate_1_animate.html
**/
animate: function (element, animation, options) {
element = Common.elOrSelector(element);
if (typeof options === 'number' || typeof options === 'string') {
options = { duration: options };
} else if (!options) {
options = {};
}
if (typeof arguments[3] === 'function') {
options.onEnd = arguments[3];
}
if (typeof options.duration !== 'number' && typeof options.duration !== 'string') {
options.duration = 400;
}
if (!Animate.animationSupported) {
if (options.onEnd) {
setTimeout(function () {
options.onEnd(null);
}, 0);
}
return;
}
if (typeof options.duration === 'number') {
element.style[animationPrefix + 'Duration'] = options.duration + 'ms';
} else if (typeof options.duration === 'string') {
Css.addClassName(element, options.duration);
}
Css.addClassName(element, ['animated', animation]);
function onAnimationEnd(event) {
if (event.target !== element) { return; }
if (event.animationName !== animation) { return; }
if (options.onEnd) { options.onEnd(event); }
if (options.removeClass) {
Css.removeClassName(element, animation);
}
if (typeof options.duration === 'string') {
Css.removeClassName(element, options.duration);
}
element.removeEventListener(animationEndEventName, onAnimationEnd, false);
}
element.addEventListener(animationEndEventName, onAnimationEnd, false);
}
});
Common.createUIComponent(Animate);
return Animate;
});
+476
View File
@@ -0,0 +1,476 @@
/**
* Flexible Carousel
* @module Ink.UI.Carousel_1
* @version 1
*/
Ink.createModule('Ink.UI.Carousel', '1',
['Ink.UI.Common_1', 'Ink.Dom.Event_1', 'Ink.Dom.Css_1', 'Ink.Dom.Element_1', 'Ink.UI.Pagination_1', 'Ink.Dom.Browser_1', 'Ink.Dom.Selector_1'],
function(Common, InkEvent, Css, InkElement, Pagination, Browser/*, Selector*/) {
'use strict';
/*
* TODO:
* keyboardSupport
*/
function limitRange(n, min, max) {
return Math.min(max, Math.max(min, n));
}
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function (cb) {return setTimeout(cb, 1000 / 30); };
/**
* @class Ink.UI.Carousel_1
* @constructor
*
* @param {String|DOMElement} selector DOM element or element id
* @param {Object} [options] Carousel Options
* @param {Integer} [options.autoAdvance] Milliseconds to wait before auto-advancing pages. Set to 0 to disable auto-advance. Defaults to 0.
* @param {String} [options.axis] Axis of the carousel. Set to 'y' for a vertical carousel. Defaults to 'x'.
* @param {Boolean} [options.center] Flag to center the carousel horizontally.
* @param {Number} [options.initialPage] Initial index page of the carousel. Defaults to 0.
* @param {Boolean} [options.spaceAfterLastSlide=true] If there are not enough slides to fill the full width of the last page, leave white space. Defaults to `true`.
* @param {Boolean} [options.swipe] Enable swipe support if available. Defaults to true.
* @param {Mixed} [options.pagination] Either an ul element to add pagination markup to or an `Ink.UI.Pagination` instance to use.
* @param {Function} [options.onChange] Callback to be called when the page changes.
*
* @sample Ink_UI_Carousel_1.html
*/
function Carousel() {
Common.BaseUIComponent.apply(this, arguments);
}
Carousel._name = 'Carousel_1';
Carousel._optionDefinition = {
autoAdvance: ['Integer', 0],
axis: ['String', 'x'],
initialPage: ['Integer', 0],
spaceAfterLastSlide: ['Boolean', true],
hideLast: ['Boolean', false],
center: ['Boolean', false],
keyboardSupport:['Boolean', false],
pagination: ['String', null],
onChange: ['Function', null],
onInit: ['Function', function () {}],
swipe: ['Boolean', true]
// TODO exponential swipe
// TODO specify break point for next page when moving finger
};
Carousel.prototype = {
_init: function () {
this._handlers = {
paginationChange: Ink.bindMethod(this, '_onPaginationChange'),
windowResize: InkEvent.throttle(Ink.bindMethod(this, 'refit'), 200)
};
InkEvent.observe(window, 'resize', this._handlers.windowResize);
this._isY = (this._options.axis === 'y');
var ulEl = Ink.s('ul.stage', this._element);
this._ulEl = ulEl;
InkElement.removeTextNodeChildren(ulEl);
if (this._options.pagination == null) {
this._currentPage = this._options.initialPage;
}
this.refit(); // recalculate this._numPages
if (this._isY) {
// Override white-space: no-wrap which is only necessary to make sure horizontal stuff stays horizontal, but breaks stuff intended to be vertical.
this._ulEl.style.whiteSpace = 'normal';
}
if (this._options.swipe) {
InkEvent.observe(this._element, 'touchstart', Ink.bindMethod(this, '_onTouchStart'));
InkEvent.observe(this._element, 'touchmove', Ink.bindMethod(this, '_onTouchMove'));
InkEvent.observe(this._element, 'touchend', Ink.bindMethod(this, '_onTouchEnd'));
}
this._setUpPagination();
this._setUpAutoAdvance();
this._setUpHider();
this._options.onInit.call(this, this);
},
/**
* Repositions elements around.
* Measure the carousel once again, adjusting the involved elements' sizes. This is called automatically when the window resizes, in order to cater for changes from responsive media queries, for instance.
*
* @method refit
* @public
*/
refit: function() {
var _isY = this._isY;
var size = function (elm, perpendicular) {
if (!elm) { return 0; }
if (!perpendicular) {
return InkElement.outerDimensions(elm)[_isY ? 1 : 0];
} else {
return InkElement.outerDimensions(elm)[_isY ? 0 : 1];
}
};
this._liEls = Ink.ss('li.slide', this._ulEl);
var numSlides = this._liEls.length;
var contRect = this._ulEl.getBoundingClientRect();
this._ctnLength = _isY ? contRect.bottom - contRect.top : contRect.right - contRect.left;
this._elLength = size(this._liEls[0]);
this._slidesPerPage = Math.floor( this._ctnLength / this._elLength ) || 1;
if (!isFinite(this._slidesPerPage)) { this._slidesPerPage = 1; }
var numPages = Math.ceil( numSlides / this._slidesPerPage );
var numPagesChanged = this._numPages !== numPages;
this._numPages = numPages;
this._deltaLength = this._slidesPerPage * this._elLength;
this._center();
this._updateHider();
this._IE7();
if (this._pagination && numPagesChanged) {
this._pagination.setSize(this._numPages);
}
this.setPage(limitRange(this.getPage(), 0, this._numPages));
},
_setUpPagination: function () {
if (this._options.pagination) {
if (Common.isDOMElement(this._options.pagination) ||
typeof this._options.pagination === 'string') {
// if dom element or css selector string...
this._pagination = new Pagination(this._options.pagination, {
size: this._numPages,
onChange: this._handlers.paginationChange
});
} else {
// assumes instantiated pagination
this._pagination = this._options.pagination;
this._pagination._options.onChange = this._handlers.paginationChange;
this._pagination.setSize(this._numPages);
}
this._pagination.setCurrent(this._options.initialPage || 0);
} else {
this._currentPage = this._options.initialPage || 0;
}
},
_setUpAutoAdvance: function () {
if (!this._options.autoAdvance) { return; }
var self = this;
setTimeout(function autoAdvance() {
self.nextPage(true /* wrap */);
setTimeout(autoAdvance, self._options.autoAdvance);
}, this._options.autoAdvance);
},
_setUpHider: function () {
if (this._options.hideLast) {
var hiderEl = InkElement.create('div', {
className: 'hider',
insertBottom: this._element
});
hiderEl.style.position = 'absolute';
hiderEl.style[ this._isY ? 'left' : 'top' ] = '0'; // fix to top..
hiderEl.style[ this._isY ? 'right' : 'bottom' ] = '0'; // and bottom...
hiderEl.style[ this._isY ? 'bottom' : 'right' ] = '0'; // and move to the end.
this._hiderEl = hiderEl;
}
},
_center: function() {
if (!this._options.center) { return; }
var gap = Math.floor( (this._ctnLength - (this._elLength * this._slidesPerPage) ) / 2 );
var pad;
if (this._isY) {
pad = [gap, 'px 0'];
} else {
pad = ['0 ', gap, 'px'];
}
this._ulEl.style.padding = pad.join('');
},
_updateHider: function() {
if (!this._hiderEl) { return; }
if (this.getPage() === 0) {
var gap = Math.floor( this._ctnLength - (this._elLength * this._slidesPerPage) );
if (this._options.center) {
gap /= 2;
}
this._hiderEl.style[ this._isY ? 'height' : 'width' ] = gap + 'px';
} else {
this._hiderEl.style[ this._isY ? 'height' : 'width' ] = '0px';
}
},
/**
* Refits elements for IE7 because it doesn't support inline-block.
*
* @method _IE7
* @private
*/
_IE7: function () {
if (Browser.IE && '' + Browser.version.split('.')[0] === '7') {
// var numPages = this._numPages;
var slides = Ink.ss('li.slide', this._ulEl);
var stl = function (prop, val) {slides[i].style[prop] = val; };
for (var i = 0, len = slides.length; i < len; i++) {
stl('position', 'absolute');
stl(this._isY ? 'top' : 'left', (i * this._elLength) + 'px');
}
}
},
_onTouchStart: function (event) {
if (event.touches.length > 1) { return; }
this._swipeData = {
x: InkEvent.pointerX(event),
y: InkEvent.pointerY(event),
lastUlPos: null
};
var ulRect = this._ulEl.getBoundingClientRect();
this._swipeData.inUlX = this._swipeData.x - ulRect.left;
this._swipeData.inUlY = this._swipeData.y - ulRect.top;
setTransitionProperty(this._ulEl, 'none');
this._touchMoveIsFirstTouchMove = true;
},
_onTouchMove: function (event) {
if (event.touches.length > 1) { return; /* multitouch event, not my problem. */ }
var pointerX = InkEvent.pointerX(event);
var pointerY = InkEvent.pointerY(event);
var deltaY = Math.abs(pointerY - this._swipeData.y);
var deltaX = Math.abs(pointerX - this._swipeData.x);
if (this._touchMoveIsFirstTouchMove) {
this._touchMoveIsFirstTouchMove = undefined;
this._scrolling = this._isY ?
deltaX > deltaY :
deltaY > deltaX ;
if (!this._scrolling) {
this._onAnimationFrame();
}
}
if (!this._scrolling && this._swipeData) {
InkEvent.stopDefault(event);
this._swipeData.pointerPos = this._isY ? pointerY : pointerX;
}
},
_onAnimationFrame: function () {
var swipeData = this._swipeData;
if (!swipeData || this._scrolling || this._touchMoveIsFirstTouchMove) { return; }
var elRect = this._element.getBoundingClientRect();
var newPos;
if (!this._isY) {
newPos = swipeData.pointerPos - swipeData.inUlX - elRect.left;
} else {
newPos = swipeData.pointerPos - swipeData.inUlY - elRect.top;
}
this._ulEl.style[this._isY ? 'top' : 'left'] = newPos + 'px';
swipeData.lastUlPos = newPos;
requestAnimationFrame(Ink.bindMethod(this, '_onAnimationFrame'));
},
_onTouchEnd: function (event) {
if (this._swipeData && this._swipeData.pointerPos && !this._scrolling && !this._touchMoveIsFirstTouchMove) {
var snapToNext = 0.1; // swipe 10% of the way to change page
var progress = - this._swipeData.lastUlPos;
var curPage = this.getPage();
var estimatedPage = progress / this._elLength / this._slidesPerPage;
if (Math.round(estimatedPage) === curPage) {
var diff = estimatedPage - curPage;
if (Math.abs(diff) > snapToNext) {
diff = diff > 0 ? 1 : -1;
curPage += diff;
}
} else {
curPage = Math.round(estimatedPage);
}
this.setPage(curPage);
InkEvent.stopDefault(event);
}
setTransitionProperty(this._ulEl, null /* transition: left, top */);
this._swipeData = null;
this._touchMoveIsFirstTouchMove = undefined;
this._scrolling = undefined;
},
_onPaginationChange: function(pgn) {
this._setPage(pgn.getCurrent());
},
/**
* Gets the current page index
* @method getPage
* @return The current page number
**/
getPage: function () {
if (this._pagination) {
return this._pagination.getCurrent();
} else {
return this._currentPage || 0;
}
},
/**
* Sets the current page index
* @method setPage
* @param {Number} page Index of the destination page.
* @param {Boolean} [wrap] Flag to activate circular counting.
**/
setPage: function (page, wrap) {
if (wrap) {
// Pages outside the range [0..this._numPages] are wrapped.
page = page % this._numPages;
if (page < 0) { page = this._numPages - page; }
}
page = limitRange(page, 0, this._numPages - 1);
if (this._pagination) {
this._pagination.setCurrent(page);
} else {
this._setPage(page);
}
},
_setPage: function (page) {
var _lengthToGo = page * this._deltaLength;
var isLastPage = page === (this._numPages - 1);
if (!this._options.spaceAfterLastSlide && isLastPage && page > 0) {
var _itemsInLastPage = this._liEls.length - (page * this._slidesPerPage);
if(_itemsInLastPage < this._slidesPerPage) {
_lengthToGo = ((page - 1) * this._deltaLength) + (_itemsInLastPage * this._elLength);
}
}
this._ulEl.style[ this._isY ? 'top' : 'left'] =
['-', _lengthToGo, 'px'].join('');
if (this._options.onChange) {
this._options.onChange.call(this, page);
}
this._currentPage = page;
this._updateHider();
},
/**
* Goes to the next page
* @method nextPage
* @param {Boolean} [wrap] Flag to loop from last page to first page.
**/
nextPage: function (wrap) {
this.setPage(this.getPage() + 1, wrap);
},
/**
* Goes to the previous page
* @method previousPage
* @param {Boolean} [wrap] Flag to loop from first page to last page.
**/
previousPage: function (wrap) { this.setPage(this.getPage() - 1, wrap); },
/**
* Returns how many slides fit into a page
* @method getSlidesPerPage
* @return {Number} The number of slides per page
* @public
*/
getSlidesPerPage: function() {
return this._slidesPerPage;
},
/**
* Get the amount of pages in the carousel.
* @method getTotalPages
* @return {Number} The number of pages
* @public
*/
getTotalPages: function() {
return this._numPages;
},
/**
* Get the stage element (your UL with the class ".stage").
* @method getStageElm
* @public
* @return {DOMElement} Stage element
**/
getStageElm: function() {
return this._ulEl;
},
/**
* Get a list of your slides (elements with the ".slide" class inside your stage)
* @method getSlidesList
* @return {DOMElement[]} Array containing the slides.
* @public
*/
getSlidesList: function() {
return this._liEls;
},
/**
* Get the total number of slides
* @method getTotalSlides
* @return {Number} The number of slides
* @public
*/
getTotalSlides: function() {
return this.getSlidesList().length;
}
};
function setTransitionProperty(el, newTransition) {
el.style.transitionProperty =
el.style.oTransitionProperty =
el.style.msTransitionProperty =
el.style.mozTransitionProperty =
el.style.webkitTransitionProperty = newTransition;
}
Common.createUIComponent(Carousel);
return Carousel;
});
+50
View File
@@ -0,0 +1,50 @@
/**
* Closing utilities
* @module Ink.UI.Close_1
* @version 1
*/
Ink.createModule('Ink.UI.Close', '1', ['Ink.Dom.Event_1','Ink.Dom.Element_1'], function(InkEvent, InkElement) {
'use strict';
/**
* Subscribes clicks on the document.body.
* Whenever an element with the classes ".ink-close" or ".ink-dismiss" is clicked, this module finds an ancestor ".ink-alert" or ".ink-alert-block" element and removes it from the DOM.
* This module should be created only once per page.
*
* @class Ink.UI.Close
* @constructor
* @example
* <script>
* Ink.requireModules(['Ink.UI.Close_1'],function( Close ){
* new Close();
* });
* </script>
*
* @sample Ink_UI_Close_1.html
*/
var Close = function() {
InkEvent.observe(document.body, 'click', function(ev) {
var el = InkEvent.element(ev);
el = InkElement.findUpwardsByClass(el, 'ink-close') ||
InkElement.findUpwardsByClass(el, 'ink-dismiss');
if (!el) {
return; // ink-close or ink-dismiss class not found
}
var toRemove = InkElement.findUpwardsByClass(el, 'ink-alert') ||
InkElement.findUpwardsByClass(el, 'ink-alert-block') ||
el;
if (toRemove) {
InkEvent.stop(ev);
InkElement.remove(toRemove);
}
});
};
Close._name = 'Close_1';
return Close;
});
+980
View File
@@ -0,0 +1,980 @@
/**
* Auxiliar utilities for UI Modules
* @module Ink.UI.Common_1
* @version 1
*/
Ink.createModule('Ink.UI.Common', '1', ['Ink.Dom.Element_1', 'Ink.Net.Ajax_1','Ink.Dom.Css_1','Ink.Dom.Selector_1','Ink.Util.Url_1'], function(InkElement, Ajax,Css,Selector,Url) {
'use strict';
var instances = {};
var lastIdNum = 0;
var nothing = {} /* a marker, for reference comparison. */;
var keys = Object.keys || function (obj) {
var ret = [];
for (var k in obj) if (obj.hasOwnProperty(k)) {
ret.push(k);
}
return ret;
};
/**
* @namespace Ink.UI.Common_1
*/
var Common = {
/**
* Supported Ink Layouts
*
* @property Layouts
* @type Object
* @readOnly
*/
Layouts: {
TINY: 'tiny',
SMALL: 'small',
MEDIUM: 'medium',
LARGE: 'large',
XLARGE: 'xlarge'
},
/**
* Checks if an item is a valid DOM Element.
*
* @method isDOMElement
* @static
* @param {Mixed} o The object to be checked.
* @return {Boolean} True if it's a valid DOM Element.
* @example
* var el = Ink.s('#element');
* if( Ink.UI.Common.isDOMElement( el ) === true ){
* // It is a DOM Element.
* } else {
* // It is NOT a DOM Element.
* }
*/
isDOMElement: function(o) {
return o && typeof o === 'object' && 'nodeType' in o && o.nodeType === 1;
},
/**
* Checks if an item is a valid integer.
*
* @method isInteger
* @static
* @param {Mixed} n The value to be checked.
* @return {Boolean} True if it's a valid integer.
* @example
* var value = 1;
* if( Ink.UI.Common.isInteger( value ) === true ){
* // It is an integer.
* } else {
* // It is NOT an integer.
* }
*/
isInteger: function(n) {
return (typeof n === 'number' && n % 1 === 0);
},
/**
* Gets a DOM Element.
*
* @method elOrSelector
* @static
* @param {DOMElement|String} elOrSelector DOM Element or CSS Selector
* @param {String} fieldName The name of the field. Commonly used for debugging.
* @return {DOMElement} Returns the DOMElement passed or the first result of the CSS Selector. Otherwise it throws an exception.
* @example
* // In case there are several .myInput, it will retrieve the first found
* var el = Ink.UI.Common.elOrSelector('.myInput','My Input');
*/
elOrSelector: function(elOrSelector, fieldName) {
if (!this.isDOMElement(elOrSelector)) {
var t = Selector.select(elOrSelector);
if (t.length === 0) {
Ink.warn(fieldName + ' must either be a DOM Element or a selector expression!\nThe script element must also be after the DOM Element itself.');
return null;
}
return t[0];
}
return elOrSelector;
},
/**
* Alias for `elOrSelector` but returns an array of elements.
*
* @method elsOrSelector
*
* @static
* @param {DOMElement|String} elOrSelector DOM Element or CSS Selector
* @param {String} fieldName The name of the field. Commonly used for debugging.
* @return {DOMElement} Returns the DOMElement passed or the first result of the CSS Selector. Otherwise it throws an exception.
* @param {Boolean} required Flag to accept an empty array as output.
* @return {Array} The selected DOM Elements.
* @example
* var elements = Ink.UI.Common.elsOrSelector('input.my-inputs', 'My Input');
*/
elsOrSelector: function(elsOrSelector, fieldName, required) {
var ret;
if (typeof elsOrSelector === 'string') {
ret = Selector.select(elsOrSelector);
} else if (Common.isDOMElement(elsOrSelector)) {
ret = [elsOrSelector];
} else if (elsOrSelector && typeof elsOrSelector === 'object' && typeof elsOrSelector.length === 'number') {
ret = elsOrSelector;
}
if (ret && ret.length) {
return ret;
} else {
if (required) {
throw new TypeError(fieldName + ' must either be a DOM Element, an Array of elements, or a selector expression!\nThe script element must also be after the DOM Element itself.');
} else {
return [];
}
}
},
/**
* Gets options an object and element's metadata.
*
* The element's data attributes take precedence. Values from the element's data-atrributes are coerced into the required type.
*
* @method options
*
* @param {Object} [fieldId] Name to be used in debugging features.
* @param {Object} defaults Object with the options' types and defaults.
* @param {Object} overrides Options to override the defaults. Usually passed when instantiating an UI module.
* @param {DOMElement} [element] Element with data-attributes
*
* @example
*
* this._options = Ink.UI.Common.options('MyComponent', {
* 'anobject': ['Object', null], // Defaults to null
* 'target': ['Element', null],
* 'stuff': ['Number', 0.1],
* 'stuff2': ['Integer', 0],
* 'doKickFlip': ['Boolean', false],
* 'targets': ['Elements'], // Required option since no default was given
* 'onClick': ['Function', null]
* }, options || {}, elm)
*
* @example
*
* ### Note about booleans
*
* Here is how options are read from the markup
* data-attributes, for several values`data-a-boolean`.
*
* Options considered true:
*
* - `data-a-boolean="true"`
* - (Every other value which is not on the list below.)
*
* Options considered false:
*
* - `data-a-boolean="false"`
* - `data-a-boolean=""`
* - `data-a-boolean`
*
* Options which go to default:
*
* - (no attribute). When `data-a-boolean` is ommitted, the
* option is not considered true nor false, and as such
* defaults to what is in the `defaults` argument.
*
**/
options: function (fieldId, defaults, overrides, element) {
if (typeof fieldId !== 'string') {
element = overrides;
overrides = defaults;
defaults = fieldId;
fieldId = '';
}
overrides = overrides || {};
var out = {};
var dataAttrs = element ? InkElement.data(element) : {};
var fromDataAttrs;
var type;
var lType;
var defaultVal;
var invalidStr = function (str) {
if (fieldId) { str = fieldId + ': "' + ('' + str).replace(/"/, '\\"') + '"'; }
return str;
};
var quote = function (str) {
return '"' + ('' + str).replace(/"/, '\\"') + '"';
};
var invalidThrow = function (str) {
throw new Error(invalidStr(str));
};
var invalid = function (str) {
Ink.error(invalidStr(str) + '. Ignoring option.');
};
function optionValue(key) {
type = defaults[key][0];
lType = type.toLowerCase();
defaultVal = defaults[key].length === 2 ? defaults[key][1] : nothing;
if (!type) {
invalidThrow('Ink.UI.Common.options: Always specify a type!');
}
if (!(lType in Common._coerce_funcs)) {
invalidThrow('Ink.UI.Common.options: ' + defaults[key][0] + ' is not a valid type. Use one of ' + keys(Common._coerce_funcs).join(', '));
}
if (!defaults[key].length || defaults[key].length > 2) {
invalidThrow('the "defaults" argument must be an object mapping option names to [typestring, optional] arrays.');
}
if (key in dataAttrs) {
fromDataAttrs = Common._coerce_from_string(lType, dataAttrs[key], key, fieldId);
// (above can return `nothing`)
} else {
fromDataAttrs = nothing;
}
if (fromDataAttrs !== nothing) {
if (!Common._options_validate(fromDataAttrs, lType)) {
invalid('(' + key + ' option) Invalid ' + lType + ' ' + quote(fromDataAttrs));
return defaultVal;
} else {
return fromDataAttrs;
}
} else if (key in overrides) {
return overrides[key];
} else if (defaultVal !== nothing) {
return defaultVal;
} else {
invalidThrow('Option ' + key + ' is required!');
}
}
for (var key in defaults) {
if (defaults.hasOwnProperty(key)) {
out[key] = optionValue(key);
}
}
return out;
},
_coerce_from_string: function (type, val, paramName, fieldId) {
if (type in Common._coerce_funcs) {
return Common._coerce_funcs[type](val, paramName, fieldId);
} else {
return val;
}
},
_options_validate: function (val, type) {
if (type in Common._options_validate_types) {
return Common._options_validate_types[type].call(Common, val);
} else {
// 'object' options cannot be passed through data-attributes.
// Json you say? Not any good to embed in HTML.
return false;
}
},
_coerce_funcs: (function () {
var ret = {
element: function (val) {
return Common.elOrSelector(val, '');
},
elements: function (val) {
return Common.elsOrSelector(val, '', false /*not required, so don't throw an exception now*/);
},
object: function (val) { return val; },
number: function (val) { return parseFloat(val); },
'boolean': function (val) {
return !(val === 'false' || val === '' || val === null);
},
string: function (val) { return val; },
'function': function (val, paramName, fieldId) {
Ink.error(fieldId + ': You cannot specify the option "' + paramName + '" through data-attributes because it\'s a function');
return nothing;
}
};
ret['float'] = ret.integer = ret.number;
return ret;
}()),
_options_validate_types: (function () {
var types = {
string: function (val) {
return typeof val === 'string';
},
number: function (val) {
return typeof val === 'number' && !isNaN(val) && isFinite(val);
},
integer: function (val) {
return val === Math.round(val);
},
element: function (val) {
return Common.isDOMElement(val);
},
elements: function (val) {
return val && typeof val === 'object' && typeof val.length === 'number' && val.length;
},
'boolean': function (val) {
return typeof val === 'boolean';
}
};
types['float'] = types.number;
return types;
}()),
/**
* Deep copy (clone) an object.
* Note: The object cannot have referece loops.
*
* @method clone
* @static
* @param {Object} o The object to be cloned/copied.
* @return {Object} Returns the result of the clone/copy.
* @example
* var originalObj = {
* key1: 'value1',
* key2: 'value2',
* key3: 'value3'
* };
* var cloneObj = Ink.UI.Common.clone( originalObj );
*/
clone: function(o) {
try {
return JSON.parse( JSON.stringify(o) );
} catch (ex) {
throw new Error('Given object cannot have loops!');
}
},
/**
* Gets an element's one-base index relative to its parent.
*
* @method childIndex
* @static
* @param {DOMElement} childEl Valid DOM Element.
* @return {Number} Numerical position of an element relatively to its parent.
* @example
* <!-- Imagine the following HTML: -->
* <ul>
* <li>One</li>
* <li>Two</li>
* <li id="test">Three</li>
* <li>Four</li>
* </ul>
*
* <script>
* var testLi = Ink.s('#test');
* Ink.UI.Common.childIndex( testLi ); // Returned value: 3
* </script>
*/
childIndex: function(childEl) {
if( Common.isDOMElement(childEl) ){
var els = Selector.select('> *', childEl.parentNode);
for (var i = 0, f = els.length; i < f; ++i) {
if (els[i] === childEl) {
return i;
}
}
}
throw 'not found!';
},
/**
* AJAX JSON request shortcut method
* It provides a more convenient way to do an AJAX request and expect a JSON response.It also offers a callback option, as third parameter, for better async handling.
*
* @method ajaxJSON
* @static
* @async
* @param {String} endpoint Valid URL to be used as target by the request.
* @param {Object} params This field is used in the thrown Exception to identify the parameter.
* @param {Function} cb Callback for the request.
* @example
* // In case there are several .myInput, it will retrieve the first found
* var el = Ink.UI.Common.elOrSelector('.myInput','My Input');
*/
ajaxJSON: function(endpoint, params, cb) {
new Ajax(
endpoint,
{
evalJS: 'force',
method: 'POST',
parameters: params,
onSuccess: function( r) {
try {
r = r.responseJSON;
if (r.status !== 'ok') {
throw 'server error: ' + r.message;
}
cb(null, r);
} catch (ex) {
cb(ex);
}
},
onFailure: function() {
cb('communication failure');
}
}
);
},
/**
* Gets the current Ink layout.
*
* @method currentLayout
* @static
* @return {String} A string representation of the current layout name.
* @example
* var inkLayout = Ink.UI.Common.currentLayout();
* if (inkLayout === 'small') {
* // ...
* }
*/
currentLayout: function() {
var i, f, k, v, el, detectorEl = Selector.select('#ink-layout-detector')[0];
if (!detectorEl) {
detectorEl = document.createElement('div');
detectorEl.id = 'ink-layout-detector';
for (k in this.Layouts) {
if (this.Layouts.hasOwnProperty(k)) {
v = this.Layouts[k];
el = document.createElement('div');
el.className = 'show-' + v + ' hide-all';
el.setAttribute('data-ink-layout', v);
detectorEl.appendChild(el);
}
}
document.body.appendChild(detectorEl);
}
for (i = 0, f = detectorEl.children.length; i < f; ++i) {
el = detectorEl.children[i];
if (Css.getStyle(el, 'display') === 'block') {
return el.getAttribute('data-ink-layout');
}
}
return 'large';
},
/**
* Sets the location's hash (window.location.hash).
*
* @method hashSet
* @static
* @param {Object} o Object with the info to be placed in the location's hash.
* @example
* // It will set the location's hash like: <url>#key1=value1&key2=value2&key3=value3
* Ink.UI.Common.hashSet({
* key1: 'value1',
* key2: 'value2',
* key3: 'value3'
* });
*/
hashSet: function(o) {
if (typeof o !== 'object') { throw new TypeError('o should be an object!'); }
var hashParams = Url.getAnchorString();
hashParams = Ink.extendObj(hashParams, o);
window.location.hash = Url.genQueryString('', hashParams).substring(1);
},
/**
* Removes children nodes from a given object.
* This method was initially created to help solve a problem in Internet Explorer(s) that occurred when trying to set the innerHTML of some specific elements like 'table'.
*
* @method cleanChildren
* @static
* @param {DOMElement} parentEl Valid DOM Element
* @example
* <!-- Imagine the following HTML: -->
* <ul id="myUl">
* <li>One</li>
* <li>Two</li>
* <li>Three</li>
* <li>Four</li>
* </ul>
*
* <script>
* Ink.UI.Common.cleanChildren( Ink.s( '#myUl' ) );
* </script>
*
* <!-- After running it, the HTML changes to: -->
* <ul id="myUl"></ul>
*/
cleanChildren: function(parentEl) {
if( !Common.isDOMElement(parentEl) ){
throw 'Please provide a valid DOMElement';
}
var prevEl, el = parentEl.lastChild;
while (el) {
prevEl = el.previousSibling;
parentEl.removeChild(el);
el = prevEl;
}
},
/**
* Stores the id and/or classes of an element in an object.
*
* @method storeIdAndClasses
* @static
* @param {DOMElement} fromEl Valid DOM Element to get the id and classes from.
* @param {Object} inObj Object where the id and classes will be saved.
* @example
* <div id="myDiv" class="aClass"></div>
*
* <script>
* var storageObj = {};
* Ink.UI.Common.storeIdAndClasses( Ink.s('#myDiv'), storageObj );
* // storageObj changes to:
* {
* _id: 'myDiv',
* _classes: 'aClass'
* }
* </script>
*/
storeIdAndClasses: function(fromEl, inObj) {
if( !Common.isDOMElement(fromEl) ){
throw 'Please provide a valid DOMElement as first parameter';
}
var id = fromEl.id;
if (id) {
inObj._id = id;
}
var classes = fromEl.className;
if (classes) {
inObj._classes = classes;
}
},
/**
* Sets the id and className properties of an element based
*
* @method restoreIdAndClasses
* @static
* @param {DOMElement} toEl Valid DOM Element to set the id and classes on.
* @param {Object} inObj Object where the id and classes to be set are. This method uses the same format as the one given in `storeIdAndClasses`
* @example
* <div></div>
*
* <script>
* var storageObj = {
* _id: 'myDiv',
* _classes: 'aClass'
* };
*
* Ink.UI.Common.storeIdAndClasses( Ink.s('div'), storageObj );
* </script>
*
* <!-- After the code runs the div element changes to: -->
* <div id="myDiv" class="aClass"></div>
*/
restoreIdAndClasses: function(toEl, inObj) {
if( !Common.isDOMElement(toEl) ){
throw 'Please provide a valid DOMElement as first parameter';
}
if (inObj._id && toEl.id !== inObj._id) {
toEl.id = inObj._id;
}
if (inObj._classes && toEl.className.indexOf(inObj._classes) === -1) {
if (toEl.className) { toEl.className += ' ' + inObj._classes; }
else { toEl.className = inObj._classes; }
}
if (inObj._instanceId && !toEl.getAttribute('data-instance')) {
toEl.setAttribute('data-instance', inObj._instanceId);
}
},
_warnDoubleInstantiation: function (elm, newInstance) {
var instances = Common.getInstance(elm);
if (getName(newInstance) === '') { return; }
if (!instances) { return; }
var nameWithoutVersion = getName(newInstance);
if (!nameWithoutVersion) { return; }
for (var i = 0, len = instances.length; i < len; i++) {
if (nameWithoutVersion === getName(instances[i])) {
// Yes, I am using + to concatenate and , to split
// arguments.
//
// Elements can't be concatenated with strings, but if
// they are passed in an argument, modern debuggers will
// pretty-print them and make it easy to find them in the
// element inspector.
//
// On the other hand, if strings are passed as different
// arguments, they get pretty printed. And the pretty
// print of a string has quotes around it.
//
// If some day people find out that strings are not
// just text and they start preserving contextual
// information, then by all means change this to a
// regular concatenation.
//
// But they won't. So don't change this.
Ink.warn('Creating more than one ' + nameWithoutVersion + '.',
'(Was creating a ' + nameWithoutVersion + ' on:', elm, ').');
return false;
}
}
function getName(thing) {
return ((thing.constructor && (thing.constructor._name)) ||
thing._name ||
'').replace(/_.*?$/, '');
}
return true;
},
/**
* Saves a component's instance reference for later retrieval.
*
* @method registerInstance
* @static
* @param {Object} inst Object that holds the instance.
* @param {DOMElement} el DOM Element to associate with the object.
*/
registerInstance: function(inst, el) {
if (!inst) { return; }
if (!this.isDOMElement(el)) { throw new TypeError('Ink.UI.Common.registerInstance: The element passed in is not a DOM element!'); }
if (Common._warnDoubleInstantiation(el, inst) === false) {
return false;
}
var id = 'instance' + (++lastIdNum);
instances[id] = inst;
inst._instanceId = id;
var dataInst = el.getAttribute('data-instance');
dataInst = (dataInst !== null) ? [dataInst, id].join(' ') : id;
el.setAttribute('data-instance', dataInst);
return true;
},
/**
* Deletes an instance with a given id.
*
* @method unregisterInstance
* @static
* @param {String} id Id of the instance to be destroyed.
*/
unregisterInstance: function(id) {
delete instances[id];
},
/**
* Gets an UI instance from an element or instance id.
*
* @method getInstance
* @static
* @param {String|DOMElement} instanceIdOrElement Instance's id or DOM Element from which we want the instances.
* @return {Object|Array} Returns an instance or a collection of instances.
*/
getInstance: function(instanceIdOrElement) {
var ids;
instanceIdOrElement = Common.elOrSelector(instanceIdOrElement);
if (instanceIdOrElement) {
ids = instanceIdOrElement.getAttribute('data-instance');
if (ids === null) { return null; }
}
else {
ids = instanceIdOrElement;
}
ids = ids.split(/\s+/g);
var inst, id, i, l = ids.length;
var res = [];
for (i = 0; i < l; ++i) {
id = ids[i];
if (!id) { throw new Error('Element is not a JS instance!'); }
inst = instances[id];
if (!inst) { throw new Error('Instance "' + id + '" not found!'); }
res.push(inst);
}
return res;
},
/**
* Gets an instance based on a selector.
*
* @method getInstanceFromSelector
* @static
* @param {String} selector CSS selector to get the instances from.
* @return {Object|Array} Returns an instance or a collection of instances.
*/
getInstanceFromSelector: function(selector) {
var el = Selector.select(selector)[0];
if (!el) { throw new Error('Element not found!'); }
return this.getInstance(el);
},
/**
* Gets all the instance ids
*
* @method getInstanceIds
* @static
* @return {Array} Collection of instance ids
*/
getInstanceIds: function() {
var res = [];
for (var id in instances) {
if (instances.hasOwnProperty(id)) {
res.push( id );
}
}
return res;
},
/**
* Gets all the instances
*
* @method getInstances
* @static
* @return {Array} Collection of existing instances.
*/
getInstances: function() {
var res = [];
for (var id in instances) {
if (instances.hasOwnProperty(id)) {
res.push( instances[id] );
}
}
return res;
},
/**
* Boilerplate method to destroy a component.
* Components should copy this method as its destroy method and modify it.
*
* @method destroyComponent
* @static
*/
destroyComponent: function() {
Common.unregisterInstance(this._instanceId);
this._element.parentNode.removeChild(this._element);
}
};
/**
* Ink UI Base Class
**/
function warnStub() {
/* jshint validthis: true */
if (!this || this === window || typeof this.constructor !== 'function') { return; }
Ink.warn('You called a method on an incorrectly instantiated ' + this.constructor._name + ' component. Check the warnings above to see what went wrong.');
}
function stub(prototype, obj) {
for (var k in prototype) if (prototype.hasOwnProperty(k)) {
if (k === 'constructor') { continue; }
if (typeof obj[k] === 'function') {
obj[k] = warnStub;
}
}
}
function BaseUIComponent(element, options) {
var constructor = this.constructor;
var _name = constructor._name;
if (this.constructor === BaseUIComponent) {
// The caller was "someModule.prototype = new BaseUIComponent" (below, on this module)
// so it doesn't make sense to construct anything.
return;
}
if (!this || this === window) {
throw new Error('Use "new InkComponent()" instead of "InkComponent()"');
}
if (this && !(this instanceof BaseUIComponent)) {
throw new Error('You forgot to call Ink.UI.Common.createUIComponent() on this module!');
}
if (!element && !constructor._componentOptions.elementIsOptional) {
Ink.error(new Error(_name + ': You need to pass an element or a selector as the first argument to "new ' + _name + '()"'));
return;
} else {
this._element = Common.elsOrSelector(element,
_name + ': An element with the selector "' + element + '" was not found!')[0];
}
if (!this._element && !constructor._componentOptions.elementIsOptional) {
isValidInstance = false;
Ink.error(new Error(element + ' does not match an element on the page. You need to pass a valid selector to "new ' + _name + '".'));
}
// TODO Change Common.options's signature? the below looks better, more manageable
// var options = Common.options({
// element: this._element,
// modName: constructor._name,
// options: constructor._optionDefinition,
// defaults: constructor._globalDefaults
// });
this._options = Common.options(_name, constructor._optionDefinition, options, this._element);
var isValidInstance = BaseUIComponent._validateInstance(this) === true;
if (isValidInstance && typeof this._init === 'function') {
try {
this._init.apply(this, arguments);
} catch(e) {
isValidInstance = false;
Ink.error(e);
}
}
if (!isValidInstance) {
BaseUIComponent._stubInstance(this, constructor, _name);
} else if (this._element) {
Common.registerInstance(this, this._element);
}
}
/**
* Calls the `instance`'s _validate() method so it can validate itself.
*
* Returns false if the method exists, was called, but no Error was returned or thrown.
*
* @method _validateInstance
* @private
*/
BaseUIComponent._validateInstance = function (instance) {
var err;
if (typeof instance._validate !== 'function') { return true; }
try {
err = instance._validate();
} catch (e) {
err = e;
}
if (err instanceof Error) {
instance._validationError = err;
return false;
}
return true;
};
/**
* Replaces every method in the instance with stub functions which just call Ink.warn().
*
* This avoids breaking the page when there are errors.
*
* @method _stubInstance
* @param instance
* @param constructor
* @param name
* @private
*/
BaseUIComponent._stubInstance = function (instance, constructor, name) {
stub(constructor.prototype, instance);
stub(BaseUIComponent.prototype, instance);
Ink.warn(name + ' was not correctly created. ' + (instance._validationError || ''));
};
// TODO BaseUIComponent.setGlobalOptions = function () {}
// TODO BaseUIComponent.createMany = function (selector) {}
Ink.extendObj(BaseUIComponent.prototype, {
getOption: function (name) {
if (this.constructor && !(name in this.constructor._optionDefinition)) {
Ink.error('"' + name + '" is not an option for ' + this.constructor._name);
return undefined;
}
return this._options[name];
},
getElement: function () {
return this._element;
}
});
Common.BaseUIComponent = BaseUIComponent;
/**
* @method createUIComponent
* @param theConstructor UI component constructor. It should have an _init function in its prototype, an _optionDefinition object, and a _name property indicating its name.
* @param options
* @param [options.elementIsOptional=false] Whether the element argument is optional (For example, when the component might work on existing markup or create its own).
**/
Common.createUIComponent = function createUIComponent(theConstructor, options) {
theConstructor._componentOptions = options || {};
function assert(test, msg) {
if (!test) {
throw new Error('Ink.UI_1.createUIComponent: ' + msg);
}
}
function assertProp(prop, propType, message) {
var propVal = theConstructor[prop];
// Check that the property was passed
assert(typeof propVal !== 'undefined',
theConstructor + ' doesn\'t have a "' + prop + '" property. ' + message);
// Check that its type is correct
assert(propType && typeof propVal === propType,
'typeof ' + theConstructor + '.' + prop + ' is not "' + propType + '". ' + message);
}
assert(typeof theConstructor === 'function',
'constructor argument is not a function!');
assertProp('_name', 'string', 'This property is used for error ' +
'messages. Set it to the full module path and version (Ink.My.Module_1).');
assertProp('_optionDefinition', 'object', 'This property contains the ' +
'option names, types and defaults. See Ink.UI.Common.options() for reference.');
// Extend the instance methods and props
var _oldProto = theConstructor.prototype;
theConstructor.prototype = new BaseUIComponent();
Ink.extendObj(theConstructor.prototype, _oldProto);
theConstructor.prototype.constructor = theConstructor;
// Extend static methods
Ink.extendObj(theConstructor, BaseUIComponent);
};
return Common;
});
File diff suppressed because it is too large Load Diff
+448
View File
@@ -0,0 +1,448 @@
/**
* Dragging elements around
* @module Ink.UI.Draggable_1
* @version 1
*/
Ink.createModule("Ink.UI.Draggable","1",["Ink.Dom.Element_1", "Ink.Dom.Event_1", "Ink.Dom.Css_1", "Ink.Dom.Browser_1", "Ink.Dom.Selector_1", "Ink.UI.Common_1"],function( InkElement, InkEvent, Css, Browser, Selector, Common) {
'use strict';
var x = 0,
y = 1; // For accessing coords in [x, y] arrays
// Get a value between two boundaries
function between (val, min, max) {
val = Math.min(val, max);
val = Math.max(val, min);
return val;
}
/**
* @class Ink.UI.Draggable
* @version 1
* @constructor
* @param {String|DOMElement} target Target element.
* @param {Object} [options] Optional object to configure the component.
* @param {String} [options.constraint] Movement constraint. None by default. Can be `vertical`, `horizontal`, or `both`.
* @param {String|DOMElement} [options.constraintElm] Constrain dragging to be within this element. None by default.
* @param {Number} [options.top] Limits to constrain draggable movement.
* @param {Number} [options.right] Limits to constrain draggable movement.
* @param {Number} [options.bottom] Limits to constrain draggable movement.
* @param {Number} [options.left] Limits to constrain draggable movement.
* @param {String|DOMElement} [options.handle] If specified, this element or CSS ID will be used as a handle for dragging.
* @param {Boolean} [options.revert] Flag to revert the draggable to the original position when dragging stops.
* @param {String} [options.cursor] Cursor type (CSS `cursor` value) used when the mouse is over the draggable object.
* @param {Number} [options.zIndex] Z-index applied to the draggable element while dragged.
* @param {Number} [options.fps] If set, throttles the drag effect to this number of frames per second.
* @param {DOMElement} [options.droppableProxy] If set, a shallow copy of this element will be moved around with transparent background.
* @param {String} [options.mouseAnchor] Anchor for the drag. Can be one of: 'left','center','right','top','center','bottom'.
* @param {String} [options.dragClass] Class to add when the draggable is being dragged. Defaults to drag.
* @param {Boolean} [options.skipChildren=true] Whether you have to drag the actual element, or dragging one of the children is okay too.
* @param {Function} [options.onStart] Callback called when dragging starts.
* @param {Function} [options.onEnd] Callback called when dragging stops.
* @param {Function} [options.onDrag] Callback called while dragging, prior to position updates.
* @param {Function} [options.onChange] Callback called while dragging, after position updates.
*
* @sample Ink_UI_Draggable_1.html
*/
function Draggable() {
Common.BaseUIComponent.apply(this, arguments);
}
Draggable._name = 'Draggable_1';
Draggable._optionDefinition = {
constraint: ['String', false],
constraintElm: ['Element', false],
top: ['Number', false],
right: ['Number', false],
bottom: ['Number', false],
left: ['Number', false],
handle: ['Element', false],
revert: ['Boolean', false],
cursor: ['String', 'move'],
zIndex: ['Number', 9999],
fps: ['Number', 100],
droppableProxy: ['Element', false],
mouseAnchor: ['String', undefined],
dragClass: ['String', 'drag'],
skipChildren: ['Boolean', true], // Magic/More Magic
onStart: ['Function', false],
onEnd: ['Function', false],
onDrag: ['Function', false],
onChange: ['Function', false]
};
Draggable.prototype = {
/**
* Init function called by the constructor
*
* @method _init
* @param {String|DOMElement} element Element ID of the element or DOM Element.
* @param {Object} [options] Options object for configuration of the module.
* @private
*/
_init: function() {
var o = this.options = this._options;
this.element = this._element;
this.constraintElm = o.constraintElm && Common.elOrSelector(o.constraintElm);
this.handle = false;
this.elmStartPosition = false;
this.active = false;
this.dragged = false;
this.prevCoords = false;
this.placeholder = false;
this.position = false;
this.zindex = false;
this.firstDrag = true;
if (o.fps) {
this.deltaMs = 1000 / o.fps;
this.lastRunAt = 0;
}
this.handlers = {};
this.handlers.start = Ink.bindEvent(this._onStart,this);
this.handlers.dragFacade = Ink.bindEvent(this._onDragFacade,this);
this.handlers.drag = Ink.bindEvent(this._onDrag,this);
this.handlers.end = Ink.bindEvent(this._onEnd,this);
this.handlers.selectStart = function(event) { InkEvent.stop(event); return false; };
// set handle
this.handle = (this.options.handle) ?
Common.elOrSelector(this.options.handle) :
this.element;
this.handle.style.cursor = o.cursor;
InkEvent.observe(this.handle, 'touchstart', this.handlers.start);
InkEvent.observe(this.handle, 'mousedown', this.handlers.start);
if (Browser.IE) {
InkEvent.observe(this.element, 'selectstart', this.handlers.selectStart);
}
},
/**
* Removes the ability of the element of being dragged
*
* @method destroy
* @public
*/
destroy: function() {
InkEvent.stopObserving(this.handle, 'touchstart', this.handlers.start);
InkEvent.stopObserving(this.handle, 'mousedown', this.handlers.start);
if (Browser.IE) {
InkEvent.stopObserving(this.element, 'selectstart', this.handlers.selectStart);
}
},
/**
* Gets coordinates for a given event (with added page scroll)
*
* @method _getCoords
* @param {Object} e window.event object.
* @return {Array} Array where the first position is the x coordinate, the second is the y coordinate
* @private
*/
_getCoords: function(e) {
var ps = [InkElement.scrollWidth(), InkElement.scrollHeight()];
return {
x: (e.touches ? e.touches[0].clientX : e.clientX) + ps[x],
y: (e.touches ? e.touches[0].clientY : e.clientY) + ps[y]
};
},
/**
* Clones src element's relevant properties to dst
*
* @method _cloneStyle
* @param {DOMElement} src Element from where we're getting the styles
* @param {DOMElement} dst Element where we're placing the styles.
* @private
*/
_cloneStyle: function(src, dst) {
dst.className = src.className;
dst.style.borderWidth = '0';
dst.style.padding = '0';
dst.style.position = 'absolute';
dst.style.width = InkElement.elementWidth(src) + 'px';
dst.style.height = InkElement.elementHeight(src) + 'px';
dst.style.left = InkElement.elementLeft(src) + 'px';
dst.style.top = InkElement.elementTop(src) + 'px';
dst.style.cssFloat = Css.getStyle(src, 'float');
dst.style.display = Css.getStyle(src, 'display');
},
/**
* onStart event handler
*
* @method _onStart
* @param {Object} e window.event object
* @return {Boolean|void} In some cases return false. Otherwise is void
* @private
*/
_onStart: function(e) {
if (!this.active && InkEvent.isLeftClick(e) || typeof e.button === 'undefined') {
var tgtEl = InkEvent.element(e);
if (this.options.skipChildren && tgtEl !== this.handle) { return; }
InkEvent.stop(e);
Css.addClassName(this.element, this.options.dragClass);
this.elmStartPosition = [
InkElement.elementLeft(this.element),
InkElement.elementTop( this.element)
];
var pos = [
parseInt(Css.getStyle(this.element, 'left'), 10),
parseInt(Css.getStyle(this.element, 'top'), 10)
];
var dims = InkElement.elementDimensions(this.element);
this.originalPosition = [ pos[x] ? pos[x]: null, pos[y] ? pos[y] : null ];
this.delta = this._getCoords(e); // mouse coords at beginning of drag
this.active = true;
this.position = Css.getStyle(this.element, 'position');
this.zindex = Css.getStyle(this.element, 'zIndex');
var div = document.createElement('div');
div.style.position = this.position;
div.style.width = dims[x] + 'px';
div.style.height = dims[y] + 'px';
div.style.marginTop = Css.getStyle(this.element, 'margin-top');
div.style.marginBottom = Css.getStyle(this.element, 'margin-bottom');
div.style.marginLeft = Css.getStyle(this.element, 'margin-left');
div.style.marginRight = Css.getStyle(this.element, 'margin-right');
div.style.borderWidth = '0';
div.style.padding = '0';
div.style.cssFloat = Css.getStyle(this.element, 'float');
div.style.display = Css.getStyle(this.element, 'display');
div.style.visibility = 'hidden';
this.delta2 = [ this.delta.x - this.elmStartPosition[x], this.delta.y - this.elmStartPosition[y] ]; // diff between top-left corner of obj and mouse
if (this.options.mouseAnchor) {
var parts = this.options.mouseAnchor.split(' ');
var ad = [dims[x], dims[y]]; // starts with 'right bottom'
if (parts[0] === 'left') { ad[x] = 0; } else if(parts[0] === 'center') { ad[x] = parseInt(ad[x]/2, 10); }
if (parts[1] === 'top') { ad[y] = 0; } else if(parts[1] === 'center') { ad[y] = parseInt(ad[y]/2, 10); }
this.applyDelta = [this.delta2[x] - ad[x], this.delta2[y] - ad[y]];
}
var dragHandlerName = this.options.fps ? 'dragFacade' : 'drag';
this.placeholder = div;
if (this.options.onStart) { this.options.onStart(this.element, e); }
if (this.options.droppableProxy) { // create new transparent div to optimize DOM traversal during drag
this.proxy = document.createElement('div');
dims = [
window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
];
var fs = this.proxy.style;
fs.width = dims[x] + 'px';
fs.height = dims[y] + 'px';
fs.position = 'fixed';
fs.left = '0';
fs.top = '0';
fs.zIndex = this.options.zindex + 1;
fs.backgroundColor = '#FF0000';
Css.setOpacity(this.proxy, 0);
var firstEl = document.body.firstChild;
while (firstEl && firstEl.nodeType !== 1) { firstEl = firstEl.nextSibling; }
document.body.insertBefore(this.proxy, firstEl);
InkEvent.observe(this.proxy, 'mousemove', this.handlers[dragHandlerName]);
InkEvent.observe(this.proxy, 'touchmove', this.handlers[dragHandlerName]);
}
else {
InkEvent.observe(document, 'mousemove', this.handlers[dragHandlerName]);
}
this.element.style.position = 'absolute';
this.element.style.zIndex = this.options.zindex;
this.element.parentNode.insertBefore(this.placeholder, this.element);
this._onDrag(e);
InkEvent.observe(document, 'mouseup', this.handlers.end);
InkEvent.observe(document, 'touchend', this.handlers.end);
return false;
}
},
/**
* Function that gets the timestamp of the current run from time to time. (FPS)
*
* @method _onDragFacade
* @param {Object} window.event object.
* @private
*/
_onDragFacade: function(e) {
var now = +new Date();
if (!this.lastRunAt || now > this.lastRunAt + this.deltaMs) {
this.lastRunAt = now;
this._onDrag(e);
}
},
/**
* Function that handles the dragging movement
*
* @method _onDrag
* @param {Object} window.event object.
* @private
*/
_onDrag: function(e) {
if (this.active) {
InkEvent.stop(e);
this.dragged = true;
var mouseCoords = this._getCoords(e),
mPosX = mouseCoords.x,
mPosY = mouseCoords.y,
o = this.options,
newX = false,
newY = false;
if (this.prevCoords && mPosX !== this.prevCoords.x || mPosY !== this.prevCoords.y) {
if (o.onDrag) { o.onDrag(this.element, e); }
this.prevCoords = mouseCoords;
newX = this.elmStartPosition[x] + mPosX - this.delta.x;
newY = this.elmStartPosition[y] + mPosY - this.delta.y;
var draggableSize = InkElement.elementDimensions(this.element);
if (this.constraintElm) {
var offset = InkElement.offset(this.constraintElm);
var size = InkElement.elementDimensions(this.constraintElm);
var constTop = offset[y] + (o.top || 0),
constBottom = offset[y] + size[y] - (o.bottom || 0),
constLeft = offset[x] + (o.left || 0),
constRight = offset[x] + size[x] - (o.right || 0);
newY = between(newY, constTop, constBottom - draggableSize[y]);
newX = between(newX, constLeft, constRight - draggableSize[x]);
} else if (o.constraint) {
var right = o.right === false ? InkElement.pageWidth() - draggableSize[x] : o.right,
left = o.left === false ? 0 : o.left,
top = o.top === false ? 0 : o.top,
bottom = o.bottom === false ? InkElement.pageHeight() - draggableSize[y] : o.bottom;
if (o.constraint === 'horizontal' || o.constraint === 'both') {
newX = between(newX, left, right);
}
if (o.constraint === 'vertical' || o.constraint === 'both') {
newY = between(newY, top, bottom);
}
}
var Droppable = Ink.getModule('Ink.UI.Droppable_1');
if (this.firstDrag) {
if (Droppable) { Droppable.updateAll(); }
/*this.element.style.position = 'absolute';
this.element.style.zIndex = this.options.zindex;
this.element.parentNode.insertBefore(this.placeholder, this.element);*/
this.firstDrag = false;
}
if (newX) { this.element.style.left = newX + 'px'; }
if (newY) { this.element.style.top = newY + 'px'; }
if (Droppable) {
// apply applyDelta defined on drag init
var mouseCoords2 = this.options.mouseAnchor ?
{x: mPosX - this.applyDelta[x], y: mPosY - this.applyDelta[y]} :
mouseCoords;
Droppable.action(mouseCoords2, 'drag', e, this.element);
}
if (o.onChange) { o.onChange(this); }
}
}
},
/**
* Function that handles the end of the dragging process
*
* @method _onEnd
* @param {Object} window.event object.
* @private
*/
_onEnd: function(e) {
InkEvent.stopObserving(document, 'mousemove', this.handlers.drag);
InkEvent.stopObserving(document, 'touchmove', this.handlers.drag);
if (this.options.fps) {
this._onDrag(e);
}
Css.removeClassName(this.element, this.options.dragClass);
if (this.active && this.dragged) {
if (this.options.droppableProxy) { // remove transparent div...
document.body.removeChild(this.proxy);
}
if (this.pt) { // remove debugging element...
InkElement.remove(this.pt);
this.pt = undefined;
}
/*if (this.options.revert) {
this.placeholder.parentNode.removeChild(this.placeholder);
}*/
if(this.placeholder) {
InkElement.remove(this.placeholder);
}
if (this.options.revert) {
this.element.style.position = this.position;
if (this.zindex !== null) {
this.element.style.zIndex = this.zindex;
}
else {
this.element.style.zIndex = 'auto';
} // restore default zindex of it had none
this.element.style.left = (this.originalPosition[x]) ? this.originalPosition[x] + 'px' : '';
this.element.style.top = (this.originalPosition[y]) ? this.originalPosition[y] + 'px' : '';
}
if (this.options.onEnd) {
this.options.onEnd(this.element, e);
}
var Droppable = Ink.getModule('Ink.UI.Droppable_1');
if (Droppable) {
Droppable.action(this._getCoords(e), 'drop', e, this.element);
}
this.position = false;
this.zindex = false;
this.firstDrag = true;
}
this.active = false;
this.dragged = false;
}
};
Common.createUIComponent(Draggable);
return Draggable;
});
+218
View File
@@ -0,0 +1,218 @@
/**
* Off-canvas menu
* @module Ink.UI.Drawer_1
* @version 1
*/
Ink.createModule('Ink.UI.Drawer', '1', ['Ink.UI.Common_1', 'Ink.Dom.Loaded_1', 'Ink.Dom.Selector_1', 'Ink.Dom.Element_1', 'Ink.Dom.Event_1', 'Ink.Dom.Css_1'], function(Common, Loaded, Selector, Element, Event, Css) {
'use strict';
function elNotFound(el) {
Ink.warn( 'Ink.UI.Drawer_1: Could not find the "' +
el + '" element on this page. Please make sure it exists.' );
}
function Drawer(options) {
Common.BaseUIComponent.apply(this, [document.body, options]);
}
Drawer._name = 'Drawer_1';
Drawer._optionDefinition = {
parentSelector: ['String', '.ink-drawer'],
leftDrawer: ['String', '.left-drawer'],
leftTrigger: ['String', '.left-drawer-trigger'],
rightDrawer: ['String', '.right-drawer'],
rightTrigger: ['String', '.right-drawer-trigger'],
contentDrawer: ['String', '.content-drawer'],
closeOnContentClick: ['Boolean', true],
closeOnLinkClick: ['Boolean', true],
mode: ['String', 'push'],
sides: ['String', 'both']
};
Drawer.prototype = {
/**
* Displays off-canvas content which can be triggered by clicking elements with the 'left-drawer-trigger' and 'right-drawer-trigger', respectively.
* The left drawer has the 'left-drawer' class, and the right drawer has the 'right-drawer' class. The content drawer (EG your `<div id="main">`) must have the 'content-drawer' class. For more, see the example below, or try the sample.
* @class Ink.UI.Drawer_1
* @constructor
*
* @param {Object} [options] Configuration options.
* @xparam {String} [options.parentSelector] The class you are using in your wrapper (in the example below, it's the `body` tag.)
* @xparam {String} [options.leftDrawer] Selector for the left drawer element. This element is placed outside the screen and shown when you click the `leftTrigger` element.
* @xparam {String} [options.leftTrigger] Selector for the left drawer trigger(s). When you click this trigger, the `leftDrawer` is shown.
* @xparam {String} [options.rightDrawer] Right drawer selector. (see `options.leftDrawer`)
* @xparam {String} [options.rightTrigger] Right trigger selector (see `options.leftTrigger`)
* @xparam {String} [options.contentDrawer] Selector for the content drawer.
* @param {Boolean} [options.closeOnContentClick] Flag to close the drawer when someone clicks on the `.contentDrawer`
* @param {String} [options.mode] This can be 'push' or 'over'.
* @param {String} [options.sides] Can be 'left', 'right', or 'both'. Controls what sides have a drawer.
*
* @example
* <body class="ink-drawer">
* <div class="left-drawer">
* Right drawer content...
* </div>
* <div class="right-drawer">
* Left drawer content...
* </div>
* <div id="main-content" class="content-drawer ink-grid">
* <a class="left-drawer-trigger" href="">Open left drawer</a>
* <a class="right-drawer-trigger" href="">Open right drawer</a>
* Content...
* </div>
* </body>
*
* <script>
* Ink.requireModules(['Ink.UI.Drawer_1'], function (Drawer) {
* new Drawer();
* });
* </script>
*/
_init: function () {
// make sure we have the required elements acording to the config options
this._contentDrawers = Ink.ss(this._options.contentDrawer);
this._leftDrawer = Ink.s(this._options.leftDrawer);
this._leftTriggers = Ink.ss(this._options.leftTrigger);
this._rightDrawer = Ink.s(this._options.rightDrawer);
this._rightTriggers = Ink.ss(this._options.rightTrigger);
// The body might not have it
Css.addClassName(document.body, 'ink-drawer');
if(this._contentDrawers.length === 0) {
throw new Error('Ink.UI.Drawer_1: Could not find any "' +
this._options.contentDrawer + '" elements on this page. ' +
'Please make sure you have at least one.' );
}
switch (this._options.sides) {
case 'both':
this._triggers =
this._options.leftTrigger + ', ' +
this._options.rightTrigger + ', ' +
this._options.contentDrawer;
break;
case 'left':
this._triggers =
this._options.leftTrigger + ', ' +
this._options.contentDrawer;
break;
case 'right':
this._triggers =
this._options.rightTrigger + ', ' +
this._options.contentDrawer;
break;
}
if (this._options.sides === 'left' || this._options.sides === 'both') {
if( !this._leftDrawer ){
elNotFound(this._options.leftDrawer);
}
if(this._leftTriggers.length === 0){
elNotFound(this._options.leftTrigger);
}
} else {
if( !this._rightDrawer ){
elNotFound(this._options.rightDrawer);
}
if( this._rightTriggers.length === 0 ){
elNotFound(this._options.rightTrigger);
}
}
this._isOpen = false;
this._direction = undefined;
this._handlers = {
click: Ink.bindEvent(this._onClick, this),
afterTransition: Ink.bindEvent(this._afterTransition, this)
};
this._delay = 10;
this._addEvents();
},
/**
* Click event handler.
* Listens to the body's click event
*
* @method _onClick
* @private
**/
_onClick: function(ev){
var triggerClicked = Ink.bind(function (side) {
// When clicking on the trigger, the corresponding side is toggled.
if (this._isOpen) {
this.close();
} else {
this.open(side);
}
ev.preventDefault();
}, this);
if(Element.findUpwardsBySelector(ev.currentTarget,this._options.leftTrigger)){
// Clicked on the left trigger
triggerClicked('left');
} else if(Element.findUpwardsBySelector(ev.currentTarget,this._options.rightTrigger)){
triggerClicked('right');
} else if(Element.findUpwardsBySelector(ev.currentTarget,this._options.contentDrawer)){
// Clicked on the rest of the body
if(this._options.closeOnContentClick) {
this.close();
}
} else if (this._options.closeOnLinkClick && Element.isLink(ev.target)) {
this.close();
// No preventDefault() here
}
},
_afterTransition: function(){
if(!this._isOpen){
if(this._direction === 'left') {
Css.removeClassName(this._leftDrawer, 'show');
} else {
Css.removeClassName(this._rightDrawer, 'show');
}
}
},
_addEvents: function(){
Event.on(document.body, 'click', this._triggers + ', a[href*="#"]', this._handlers.click);
},
open: function(direction) {
this._isOpen = true;
this._direction = direction;
var open = direction === 'left' ?
this._leftDrawer :
this._rightDrawer;
Css.addClassName(open,'show');
setTimeout(Ink.bind(function(){
Css.addClassName(document.body, [this._options.mode, direction]);
},this), this._delay);
},
close: function() {
if (this._isOpen === false) { return; }
this._isOpen = false;
// TODO detect transitionEnd exists, otherwise don't rely on it
Event.one(document.body, 'transitionend oTransitionEnd webkitTransitionEnd', this._handlers.afterTransition);
Css.removeClassName(document.body, [this._options.mode, this._direction]);
}
};
Common.createUIComponent(Drawer);
return Drawer;
});
+199
View File
@@ -0,0 +1,199 @@
/**
* Dropdown menus
*
* @module Ink.UI.Dropdown_1
* Use this UI module to achieve a dropdown menu.
*
* @version 1
*/
Ink.createModule('Ink.UI.Dropdown', '1', ['Ink.UI.Common_1', 'Ink.UI.Toggle_1', 'Ink.Dom.Event_1', 'Ink.Dom.Element_1'], function(Common, Toggle, InkEvent, InkElement) {
'use strict';
function Dropdown() {
Common.BaseUIComponent.apply(this, arguments);
}
Dropdown._name = 'Dropdown_1';
Dropdown._optionDefinition = {
'target': ['Element'],
'hoverOpen': ['Number', null],
'dismissOnInsideClick': ['Boolean', false],
'dismissOnOutsideClick': ['Boolean', true],
'dismissAfter': ['Number', null],
'onInsideClick': ['Function', null],
'onOutsideClick': ['Function', null],
'onOpen': ['Function', null],
'onDismiss': ['Function', null]
};
Dropdown.prototype = {
/**
* @class Ink.UI.Dropdown
*
* @constructor
* @param {DOMElement|String} trigger Trigger Element
* @param {Object} options Options Object
* @param {DOMElement|String} options.target Target of the dropdown action.
*
* @sample Ink_UI_Dropdown_1.html
*/
_init: function() {
this._toggle = new Toggle(this._element, {
target: this._options.target,
closeOnInsideClick: null,
closeOnClick: false,
onChangeState: Ink.bind(function (newState) {
return this._openOrDismiss(newState, true, true);
}, this)
});
// Event where we set this._dismissTimeout and clear this._openTimeout
InkEvent.observeMulti([this._options.target, this._element],
'mouseout', Ink.bindMethod(this, '_onMouseOut'));
// Events to keep clearing this._dismissTimeout and set this._openTimeout
InkEvent.observeMulti([this._options.target, this._element],
'mouseover', Ink.bindMethod(this, '_onMouseOver'));
// to call dismissOnInsideClick and onInsideClick
InkEvent.observe(this._options.target, 'click', Ink.bindMethod(this, '_onInsideClick'));
// to call dismissOnOutsideClick and onOutsideClick
InkEvent.observe(document, 'click', Ink.bindMethod(this, '_onOutsideClick'));
},
/**
* Called when the mouse is over the toggler, or the dropdown.
*
* Deals with "hoverOpen" by setting the dropdown to open later. Also cancels "dismissAfter".
* @method _onMouseOver
* @private
**/
_onMouseOver: function () {
if (typeof this._options.hoverOpen === 'number' && this._toggle.getState() === false) {
clearTimeout(this._openTimeout);
this._openTimeout = setTimeout(
Ink.bindMethod(this, 'open', true),
this._options.hoverOpen * 1000);
}
if (typeof this._options.dismissAfter === 'number') {
clearTimeout(this._dismissTimeout);
}
},
/**
* Called when the mouse leaves either the toggler, or the dropdown.
*
* Deals with "dismissAfter" by setting the dropdown to be dismissed later. Also cancels "hoverOpen".
* @method _onMouseOut
* @private
**/
_onMouseOut: function () {
if (typeof this._options.dismissAfter === 'number' && this._toggle.getState() === true) {
clearTimeout(this._dismissTimeout);
this._dismissTimeout = setTimeout(
Ink.bindMethod(this, 'dismiss', true),
this._options.dismissAfter * 1000);
}
if (typeof this._options.hoverOpen === 'number') {
clearTimeout(this._openTimeout);
}
},
/**
* Handle clicks on the dropdown.
* @method _onInsideClick
* @private
*/
_onInsideClick: function (event) {
var ret = this._handlerCall('onInsideClick', InkEvent.element(event));
if (ret === false) { return; }
if (this._options.dismissOnInsideClick) {
this.dismiss(true);
}
},
/**
* Handle clicks outside the dropdown.
* @method _onOutsideClick
* @private
*/
_onOutsideClick: function (event) {
var target = InkEvent.element(event);
var foundElem = InkElement.findUpwardsHaving(target, Ink.bind(function (needle) {
return needle === this._element;
}, this));
var foundTarget = InkElement.findUpwardsHaving(target, Ink.bind(function (needle) {
return needle === this._options.target;
}, this));
if (!foundElem && !foundTarget) {
var ret = this._handlerCall('onOutsideClick', target);
if (ret === false) { return; }
if (this._options.dismissOnOutsideClick) {
this.dismiss(true);
}
}
},
/**
* Closes the dropdown.
*
* @method dismiss
* @param [callHandler=false] call onDismiss handler
*/
dismiss: function (callHandler, doNotInformToggle) {
this._openOrDismiss(false, callHandler, doNotInformToggle);
},
/**
* Opens the dropdown
*
* @method open
* @param [callHandler=false] call onOpen handler
*/
open: function (callHandler, _doNotInformToggle) {
this._openOrDismiss(true, callHandler, _doNotInformToggle);
},
/**
* DRY'ing up open() and dismiss()
*
* @method _openOrDismiss
* @param [newState=false]
* @param [callHandler=false]
* @private
*/
_openOrDismiss: function (newState, callHandler, _doNotInformToggle) {
if (this._toggle && this._toggle.getState() === newState) { return; }
if (callHandler) {
if (this._handlerCall(newState ? 'onOpen' : 'onDismiss') === false) {
return false; // canceled by event handler
}
}
if (!_doNotInformToggle) {
this._toggle.setState(newState);
}
clearTimeout(this._dismissTimeout);
clearTimeout(this._openTimeout);
},
/**
* call a method given by the user through the options
*
* @method _handlerCall
* @param handler {String} The handler name in this._options
* @param [args*] Arguments to pass to function
*/
_handlerCall: function (handler/*, ... */) {
if (this._options[handler]) {
return this._options[handler].call(this, [].slice.call(arguments, 1));
}
}
};
Common.createUIComponent(Dropdown);
return Dropdown;
});
+286
View File
@@ -0,0 +1,286 @@
/**
* Drop elements around
* @module Ink.UI.Droppable_1
* @version 1
*/
Ink.createModule("Ink.UI.Droppable","1",["Ink.Dom.Element_1", "Ink.Dom.Event_1", "Ink.Dom.Css_1", "Ink.UI.Common_1", "Ink.Util.Array_1", "Ink.Dom.Selector_1"], function( InkElement, InkEvent, Css, Common, InkArray, Selector) {
'use strict';
// Higher order functions
var hAddClassName = function (element) {
return function (className) {return Css.addClassName(element, className);};
};
var hRemoveClassName = function (element) {
return function (className) {return Css.removeClassName(element, className);};
};
/**
* @namespace Ink.UI.Droppable
* @version 1
* @static
*/
var Droppable = {
/**
* Flag to activate debug mode
*
* @property debug
* @type {Boolean}
* @private
*/
debug: false,
/**
* Array with the data of each element (`{element: ..., data: ..., options: ...}`)
*
* @property _droppables
* @type {Array}
* @private
*/
_droppables: [],
/**
* Array of data for each draggable. (`{element: ..., data: ...}`)
*
* @property _draggables
* @type {Array}
* @private
*/
_draggables: [],
/**
* Makes an element droppable.
* This method adds it to the stack of droppable elements.
* Can consider it a constructor of droppable elements, but where no Droppable object is returned.
*
* In the following arguments, any events/callbacks you may pass, can be either functions or strings. If the 'move' or 'copy' strings are passed, the draggable gets moved into this droppable. If 'revert' is passed, an acceptable droppable is moved back to the element it came from.
*
* @method add
* @param {String|DOMElement} element Target element
* @param {Object} [options] Options object
* @param {String} [options.hoverClass] Classname(s) applied when an acceptable draggable element is hovering the element
* @param {String} [options.accept] Selector for choosing draggables which can be dropped in this droppable.
* @param {Function} [options.onHover] Callback when an acceptable draggable element is hovering the droppable. Gets the draggable and the droppable element as parameters.
* @param {Function|String} [options.onDrop] Callback when an acceptable draggable element is dropped. Gets the draggable, the droppable and the event as parameters.
* @param {Function|String} [options.onDropOut] Callback when a droppable is dropped outside this droppable. Gets the draggable, the droppable and the event as parameters. (see above for string options).
* @public
*
* @sample Ink_UI_Droppable_1.html
*
*/
add: function(element, options) {
element = Common.elOrSelector(element, 'Droppable.add target element');
var opt = Ink.extendObj( {
hoverClass: options.hoverclass /* old name */ || false,
accept: false,
onHover: false,
onDrop: false,
onDropOut: false
}, options || {}, InkElement.data(element));
if (typeof opt.hoverClass === 'string') {
opt.hoverClass = opt.hoverClass.split(/\s+/);
}
function cleanStyle(draggable) {
draggable.style.position = 'inherit';
}
var that = this;
var namedEventHandlers = {
move: function (draggable, droppable/*, event*/) {
cleanStyle(draggable);
droppable.appendChild(draggable);
},
copy: function (draggable, droppable/*, event*/) {
cleanStyle(draggable);
droppable.appendChild(draggable.cloneNode);
},
revert: function (draggable/*, droppable, event*/) {
that._findDraggable(draggable).originalParent.appendChild(draggable);
cleanStyle(draggable);
}
};
var name;
if (typeof opt.onHover === 'string') {
name = opt.onHover;
opt.onHover = namedEventHandlers[name];
if (opt.onHover === undefined) {
throw new Error('Unknown hover event handler: ' + name);
}
}
if (typeof opt.onDrop === 'string') {
name = opt.onDrop;
opt.onDrop = namedEventHandlers[name];
if (opt.onDrop === undefined) {
throw new Error('Unknown drop event handler: ' + name);
}
}
if (typeof opt.onDropOut === 'string') {
name = opt.onDropOut;
opt.onDropOut = namedEventHandlers[name];
if (opt.onDropOut === undefined) {
throw new Error('Unknown dropOut event handler: ' + name);
}
}
var elementData = {
element: element,
data: {},
options: opt
};
this._droppables.push(elementData);
this._update(elementData);
},
/**
* Finds droppable data about `element`. this data is added in `.add`
*
* @method _findData
* @param {DOMElement} element Needle
* @return {object} Droppable data of the element
* @private
*/
_findData: function (element) {
var elms = this._droppables;
for (var i = 0, len = elms.length; i < len; i++) {
if (elms[i].element === element) {
return elms[i];
}
}
},
/**
* Finds draggable data about `element`
*
* @method _findDraggable
* @param {DOMElement} element Needle
* @return {Object} Draggable data queried
* @private
*/
_findDraggable: function (element) {
var elms = this._draggables;
for (var i = 0, len = elms.length; i < len; i++) {
if (elms[i].element === element) {
return elms[i];
}
}
},
/**
* Invoke every time a drag starts
*
* @method updateAll
* @private
*/
updateAll: function() {
InkArray.each(this._droppables, Droppable._update);
},
/**
* Updates location and size of droppable element
*
* @method update
* @param {String|DOMElement} element Target element
* @public
*/
update: function(element) {
this._update(this._findData(element));
},
_update: function(elementData) {
var data = elementData.data;
var element = elementData.element;
data.left = InkElement.offsetLeft(element);
data.top = InkElement.offsetTop( element);
data.right = data.left + InkElement.elementWidth( element);
data.bottom = data.top + InkElement.elementHeight(element);
},
/**
* Removes an element from the droppable stack and removes the droppable behavior
*
* @method remove
* @param {String|DOMElement} elOrSelector Droppable element to disable.
* @return {Boolean} Whether the object was found and deleted
* @public
*/
remove: function(el) {
el = Common.elOrSelector(el);
var len = this._droppables.length;
for (var i = 0; i < len; i++) {
if (this._droppables[i].element === el) {
this._droppables.splice(i, 1);
break;
}
}
return len !== this._droppables.length;
},
/**
* Executes an action on a droppable
*
* @method action
* @param {Object} coords Coordinates where the action happened
* @param {String} type Type of action. 'drag' or 'drop'.
* @param {Object} ev Event object
* @param {Object} draggable Draggable element
* @private
*/
action: function(coords, type, ev, draggable) {
// check all droppable elements
InkArray.each(this._droppables, Ink.bind(function(elementData) {
var data = elementData.data;
var opt = elementData.options;
var element = elementData.element;
if (opt.accept && !Selector.matches(opt.accept, [draggable]).length) {
return;
}
if (type === 'drag' && !this._findDraggable(draggable)) {
this._draggables.push({
element: draggable,
originalParent: draggable.parentNode
});
}
// check if our draggable is over our droppable
if (coords.x >= data.left && coords.x <= data.right &&
coords.y >= data.top && coords.y <= data.bottom) {
// INSIDE
if (type === 'drag') {
if (opt.hoverClass) {
InkArray.each(opt.hoverClass,
hAddClassName(element));
}
if (opt.onHover) {
opt.onHover(draggable, element);
}
} else if (type === 'drop') {
if (opt.hoverClass) {
InkArray.each(opt.hoverClass,
hRemoveClassName(element));
}
if (opt.onDrop) {
opt.onDrop(draggable, element, ev);
}
}
} else {
// OUTSIDE
if (type === 'drag' && opt.hoverClass) {
InkArray.each(opt.hoverClass, hRemoveClassName(element));
} else if (type === 'drop') {
if(opt.onDropOut){
opt.onDropOut(draggable, element, ev);
}
}
}
}, this));
}
};
return Droppable;
});

Some files were not shown because too many files have changed in this diff Show More