Browse Source

Updates api, state, visibility, visit to latest iteration

pull/912/head
jlukic 10 years ago
parent
commit
174ea2cc06
4 changed files with 295 additions and 104 deletions
  1. 108
      src/definitions/behaviors/api.js
  2. 4
      src/definitions/behaviors/state.js
  3. 223
      src/definitions/behaviors/visibility.js
  4. 64
      src/definitions/behaviors/visit.js

108
src/definitions/behaviors/api.js

@ -112,7 +112,6 @@ $.api = $.fn.api = function(parameters) {
module.debug('Request cancelled previous request is still pending'); module.debug('Request cancelled previous request is still pending');
return; return;
} }
// pass element metadata to url (value, text) // pass element metadata to url (value, text)
if(settings.defaultData) { if(settings.defaultData) {
$.extend(true, settings.urlData, module.get.defaultData()); $.extend(true, settings.urlData, module.get.defaultData());
@ -201,39 +200,71 @@ $.api = $.fn.api = function(parameters) {
add: { add: {
urlData: function(url, urlData) { urlData: function(url, urlData) {
var var
urlVariables
requiredVariables,
optionalVariables
; ;
if(url) { if(url) {
urlVariables = url.match(settings.regExp.required);
urlData = urlData || settings.urlData;
if(urlVariables) {
module.debug('Looking for URL variables', urlVariables);
$.each(urlVariables, function(index, templateValue){
requiredVariables = url.match(settings.regExp.required);
optionalVariables = url.match(settings.regExp.optional);
urlData = urlData || settings.urlData;
if(requiredVariables) {
module.debug('Looking for required URL variables', requiredVariables);
$.each(requiredVariables, function(index, templatedString) {
var var
term = templateValue.substr( 2, templateValue.length - 3),
termValue = ($.isPlainObject(urlData) && urlData[term] !== undefined)
? urlData[term]
: ($module.data(term) !== undefined)
? $module.data(term)
: ($context.data(term) !== undefined)
? $context.data(term)
: urlData[term]
// allow legacy {$var} style
variable = (templatedString.indexOf('$') !== -1)
? templatedString.substr(2, templatedString.length - 3)
: templatedString.substr(1, templatedString.length - 2),
value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
? urlData[variable]
: ($module.data(variable) !== undefined)
? $module.data(variable)
: ($context.data(variable) !== undefined)
? $context.data(variable)
: urlData[variable]
; ;
module.verbose('Looking for variable', term);
// remove optional value
if(termValue === false) {
module.debug('Removing variable from URL', urlVariables);
url = url.replace('/' + templateValue, '');
}
// undefined condition
else if(termValue === undefined || !termValue) {
module.error(error.missingParameter, term, url);
// remove value
if(value === undefined) {
module.error(error.requiredParameter, variable, url);
url = false; url = false;
return false; return false;
} }
else { else {
url = url.replace(templateValue, termValue);
module.verbose('Found required variable', variable, value);
url = url.replace(templatedString, value);
}
});
}
if(optionalVariables) {
module.debug('Looking for optional URL variables', requiredVariables);
$.each(optionalVariables, function(index, templatedString) {
var
// allow legacy {/$var} style
variable = (templatedString.indexOf('$') !== -1)
? templatedString.substr(3, templatedString.length - 4)
: templatedString.substr(2, templatedString.length - 3),
value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
? urlData[variable]
: ($module.data(variable) !== undefined)
? $module.data(variable)
: ($context.data(variable) !== undefined)
? $context.data(variable)
: urlData[variable]
;
// optional replacement
if(value !== undefined) {
module.verbose('Optional variable Found', variable, value);
url = url.replace(templatedString, value);
}
else {
module.verbose('Optional variable not found', variable);
// remove preceding slash if set
if(url.indexOf('/' + templatedString) !== -1) {
url = url.replace('/' + templatedString, '');
}
else {
url = url.replace(templatedString, '');
}
} }
}); });
} }
@ -683,7 +714,8 @@ $.api.settings = {
action : false, action : false,
regExp : { regExp : {
required: /\{\$([A-z]+)\}/g
required: /\{\$*[A-z0-9]+\}/g,
optional: /\{\/\$*[A-z0-9]+\}/g,
}, },
// data // data
@ -717,17 +749,17 @@ $.api.settings = {
// errors // errors
error : { error : {
beforeSend : 'The before send function has aborted the request',
error : 'There was an error with your request',
exitConditions : 'API Request Aborted. Exit conditions met',
JSONParse : 'JSON could not be parsed during error handling',
missingSerialize : 'Serializing a Form requires toJSON to be included',
missingAction : 'API action used but no url was defined',
missingParameter : 'Missing an essential URL parameter: ',
missingURL : 'No URL specified for api event',
parseError : 'There was an error parsing your request',
statusMessage : 'Server gave an error: ',
timeout : 'Your request timed out'
beforeSend : 'The before send function has aborted the request',
error : 'There was an error with your request',
exitConditions : 'API Request Aborted. Exit conditions met',
JSONParse : 'JSON could not be parsed during error handling',
missingAction : 'API action used but no url was defined',
missingSerialize : 'Serializing a Form requires toJSON to be included',
missingURL : 'No URL specified for api event',
parseError : 'There was an error parsing your request',
requiredParameter : 'Missing a required URL parameter: ',
statusMessage : 'Server gave an error: ',
timeout : 'Your request timed out'
}, },
className: { className: {

4
src/definitions/behaviors/state.js

@ -166,13 +166,13 @@ $.fn.state = function(parameters) {
$module.addClass(className.disabled); $module.addClass(className.disabled);
}, },
enableState: function(state) {
setState: function(state) {
if(module.allows(state)) { if(module.allows(state)) {
$module.addClass( className[state] ); $module.addClass( className[state] );
} }
}, },
disableState: function(state) {
removeState: function(state) {
if(module.allows(state)) { if(module.allows(state)) {
$module.removeClass( className[state] ); $module.removeClass( className[state] );
} }

223
src/definitions/behaviors/visibility.js

@ -65,7 +65,11 @@ $.fn.visibility = function(parameters) {
module.bindEvents(); module.bindEvents();
module.instantiate(); module.instantiate();
setTimeout(module.checkVisibility, settings.loadWait);
if(settings.type == 'image') {
module.setup.image();
}
requestAnimationFrame(module.checkVisibility);
}, },
instantiate: function() { instantiate: function() {
@ -88,6 +92,7 @@ $.fn.visibility = function(parameters) {
}, },
bindEvents: function() { bindEvents: function() {
module.verbose('Binding visibility events to scroll and resize');
$window $window
.on('resize', module.event.refresh) .on('resize', module.event.refresh)
.on('scroll', module.event.scroll) .on('scroll', module.event.scroll)
@ -99,25 +104,99 @@ $.fn.visibility = function(parameters) {
requestAnimationFrame(module.refresh); requestAnimationFrame(module.refresh);
}, },
scroll: function() { scroll: function() {
requestAnimationFrame(module.checkVisibility);
module.verbose('Scroll position changed');
if(settings.throttle) {
clearTimeout(module.timer);
module.timer = setTimeout(module.checkVisibility, 200);
}
else {
requestAnimationFrame(module.checkVisibility);
}
}
},
precache: function(images, callback) {
if (!(images instanceof Array)) {
images = [images];
}
var
imagesLength = images.length,
loadedCounter = 0,
cache = [],
cacheImage = document.createElement('img'),
handleLoad = function() {
loadedCounter++;
if (loadedCounter >= images.length) {
if ($.isFunction(callback)) {
callback();
}
}
}
;
while (imagesLength--) {
cacheImage = document.createElement('img');
cacheImage.onload = handleLoad;
cacheImage.onerror = handleLoad;
cacheImage.src = images[imagesLength];
cache.push(cacheImage);
}
},
setup: {
image: function() {
var
src = $module.data('src')
;
if(src) {
module.verbose('Lazy loading image', src);
settings.once = true;
// show when top visible
module.topVisible(function() {
module.precache(src, function() {
module.set.image(src);
settings.onTopVisible = false;
});
});
}
}
},
set: {
image: function(src) {
var
offScreen = (module.cache.screen.bottom + settings.offset < module.cache.element.top)
;
$module
.attr('src', src)
;
if($.fn.transition !== undefined || offScreen) {
$module.transition(settings.transition, settings.duration);
}
else {
$module.fadeIn(settings.duration);
}
} }
}, },
refresh: function() { refresh: function() {
module.debug('Refreshing constants (element width/height)');
module.reset();
module.save.position(); module.save.position();
module.checkVisibility(); module.checkVisibility();
$.proxy(settings.onRefresh, element)(); $.proxy(settings.onRefresh, element)();
}, },
reset: function() { reset: function() {
module.verbose('Reseting all cached values');
module.cache = { module.cache = {
occurred: {},
screen : {}, screen : {},
element : {} element : {}
}; };
}, },
checkVisibility: function() { checkVisibility: function() {
module.verbose('Updating visibility of element', module.cache.element);
module.verbose('Checking visibility of element', module.cache.element);
module.save.scroll(); module.save.scroll();
module.save.direction(); module.save.direction();
module.save.screenCalculations(); module.save.screenCalculations();
@ -131,24 +210,6 @@ $.fn.visibility = function(parameters) {
module.bottomPassed(); module.bottomPassed();
}, },
passing: function(newCallback) {
var
calculations = module.get.elementCalculations(),
screen = module.get.screenCalculations(),
callback = newCallback || settings.onPassing
;
if(newCallback) {
module.debug('Adding callback for passing', newCallback);
settings.onPassing = newCallback;
}
if(callback && calculations.passing) {
$.proxy(callback, element)(calculations, screen);
}
else {
return calculations.passing;
}
},
passed: function(amount, newCallback) { passed: function(amount, newCallback) {
var var
calculations = module.get.elementCalculations(), calculations = module.get.elementCalculations(),
@ -164,26 +225,54 @@ $.fn.visibility = function(parameters) {
else if(calculations.passing) { else if(calculations.passing) {
$.each(settings.onPassed, function(amount, callback) { $.each(settings.onPassed, function(amount, callback) {
if(calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) { if(calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) {
callback();
module.execute(callback, amount);
}
else {
module.remove.occurred(callback, amount);
} }
}); });
} }
}, },
passing: function(newCallback) {
var
calculations = module.get.elementCalculations(),
callback = newCallback || settings.onPassing,
callbackName = 'passing'
;
if(newCallback) {
module.debug('Adding callback for passing', newCallback);
settings.onPassing = newCallback;
}
if(callback && calculations.passing) {
module.execute(callback, callbackName);
}
else {
module.remove.occurred(callbackName);
}
if(newCallback !== undefined) {
return calculations.passing;
}
},
topVisible: function(newCallback) { topVisible: function(newCallback) {
var var
calculations = module.get.elementCalculations(), calculations = module.get.elementCalculations(),
screen = module.get.screenCalculations(),
callback = newCallback || settings.onTopVisible
callback = newCallback || settings.onTopVisible,
callbackName = 'topVisible'
; ;
if(newCallback) { if(newCallback) {
module.debug('Adding callback for top visible', newCallback); module.debug('Adding callback for top visible', newCallback);
settings.onTopVisible = newCallback; settings.onTopVisible = newCallback;
} }
if(callback && calculations.topVisible) { if(callback && calculations.topVisible) {
$.proxy(callback, element)(calculations, screen);
module.execute(callback, callbackName);
} }
else { else {
module.remove.occurred(callbackName);
}
if(newCallback === undefined) {
return calculations.topVisible; return calculations.topVisible;
} }
}, },
@ -191,17 +280,20 @@ $.fn.visibility = function(parameters) {
bottomVisible: function(newCallback) { bottomVisible: function(newCallback) {
var var
calculations = module.get.elementCalculations(), calculations = module.get.elementCalculations(),
screen = module.get.screenCalculations(),
callback = newCallback || settings.onBottomVisible
callback = newCallback || settings.onBottomVisible,
callbackName = 'bottomVisible'
; ;
if(newCallback) { if(newCallback) {
module.debug('Adding callback for bottom visible', newCallback); module.debug('Adding callback for bottom visible', newCallback);
settings.onBottomVisible = newCallback; settings.onBottomVisible = newCallback;
} }
if(callback && calculations.bottomVisible) { if(callback && calculations.bottomVisible) {
$.proxy(callback, element)(calculations, screen);
module.execute(callback, callbackName);
} }
else { else {
module.remove.occurred(callbackName);
}
if(newCallback === undefined) {
return calculations.bottomVisible; return calculations.bottomVisible;
} }
}, },
@ -209,17 +301,20 @@ $.fn.visibility = function(parameters) {
topPassed: function(newCallback) { topPassed: function(newCallback) {
var var
calculations = module.get.elementCalculations(), calculations = module.get.elementCalculations(),
screen = module.get.screenCalculations(),
callback = newCallback || settings.onTopPassed
callback = newCallback || settings.onTopPassed,
callbackName = 'topPassed'
; ;
if(newCallback) { if(newCallback) {
module.debug('Adding callback for top passed', newCallback); module.debug('Adding callback for top passed', newCallback);
settings.onTopPassed = newCallback; settings.onTopPassed = newCallback;
} }
if(callback && calculations.topPassed) { if(callback && calculations.topPassed) {
$.proxy(callback, element)(calculations, screen);
module.execute(callback, callbackName);
} }
else { else {
module.remove.occurred(callbackName);
}
if(newCallback === undefined) {
return calculations.topPassed; return calculations.topPassed;
} }
}, },
@ -227,22 +322,56 @@ $.fn.visibility = function(parameters) {
bottomPassed: function(newCallback) { bottomPassed: function(newCallback) {
var var
calculations = module.get.elementCalculations(), calculations = module.get.elementCalculations(),
screen = module.get.screenCalculations(),
callback = newCallback || settings.onBottomPassed
callback = newCallback || settings.onBottomPassed,
callbackName = 'bottomPassed'
; ;
if(newCallback) { if(newCallback) {
module.debug('Adding callback for bottom passed', newCallback); module.debug('Adding callback for bottom passed', newCallback);
settings.bottomPassed = newCallback; settings.bottomPassed = newCallback;
} }
if(callback && calculations.bottomPassed) { if(callback && calculations.bottomPassed) {
$.proxy(callback, element)(calculations, screen);
module.execute(callback, callbackName);
} }
else { else {
module.remove.occurred(callbackName);
}
if(newCallback === undefined) {
return calculations.bottomPassed; return calculations.bottomPassed;
} }
}, },
execute: function(callback, callbackName) {
var
calculations = module.get.elementCalculations(),
screen = module.get.screenCalculations()
;
if(settings.once && module.get.occurred(callbackName)) {
// multiple callbacks are ignored when times == 'once'
}
else {
module.debug('Conditions met for callback', callbackName, calculations);
$.proxy(callback, element)(calculations, screen);
}
module.save.occurred(callbackName);
},
remove: {
occurred: function(callback) {
if(callback) {
module.cache.occurred[callback] = false;
}
else {
module.cache.occurred = {};
}
}
},
save: { save: {
occurred: function(callback) {
if(callback) {
module.cache.occurred[callback] = true;
}
},
scroll: function() { scroll: function() {
module.cache.scroll = $window.scrollTop() + settings.offset; module.cache.scroll = $window.scrollTop() + settings.offset;
}, },
@ -329,8 +458,8 @@ $.fn.visibility = function(parameters) {
} }
module.save.direction(); module.save.direction();
$.extend(module.cache.screen, { $.extend(module.cache.screen, {
top : scroll + settings.offset,
bottom : scroll + settings.offset + module.cache.screen.height
top : scroll - settings.offset,
bottom : scroll - settings.offset + module.cache.screen.height
}); });
return module.cache.screen; return module.cache.screen;
}, },
@ -356,6 +485,12 @@ $.fn.visibility = function(parameters) {
} }
return parseInt(amount, 10); return parseInt(amount, 10);
}, },
occurred: function(callback) {
return (module.cache.occurred !== undefined)
? module.cache.occurred[callback] || false
: false
;
},
direction: function() { direction: function() {
if(module.cache.direction === undefined) { if(module.cache.direction === undefined) {
module.save.direction(); module.save.direction();
@ -585,12 +720,17 @@ $.fn.visibility.settings = {
verbose : false, verbose : false,
performance : true, performance : true,
loadWait : 1000,
watch : true,
offset : 0, offset : 0,
includeMargin : false, includeMargin : false,
// visibility check delay in ms
throttle : false,
// special visibility type
type : false,
transition : 'fade in',
duration : 500,
// array of callbacks // array of callbacks
onPassed : {}, onPassed : {},
@ -601,10 +741,15 @@ $.fn.visibility.settings = {
onTopPassed : false, onTopPassed : false,
onBottomPassed : false, onBottomPassed : false,
once : false,
// utility callbacks // utility callbacks
onRefresh : function(){}, onRefresh : function(){},
onScroll : function(){}, onScroll : function(){},
// not used currently waiting for (DOM Mutations API adoption)
// https://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#mutation-observers
watch : true,
watchedProperties : [ watchedProperties : [
'offsetWidth', 'offsetWidth',
'offsetHeight', 'offsetHeight',

64
src/definitions/behaviors/visit.js

@ -47,12 +47,12 @@ $.visit = $.fn.visit = function(parameters) {
module = { module = {
initialize: function() { initialize: function() {
if(settings.id) {
module.add.id(settings.id);
}
else if(settings.count) {
if(settings.count) {
module.store(settings.key.count, settings.count); module.store(settings.key.count, settings.count);
} }
else if(settings.id) {
module.add.id(settings.id);
}
else if(settings.increment && methodInvoked !== 'increment') { else if(settings.increment && methodInvoked !== 'increment') {
module.increment(); module.increment();
} }
@ -61,7 +61,7 @@ $.visit = $.fn.visit = function(parameters) {
}, },
instantiate: function() { instantiate: function() {
module.verbose('Storing instance of visit', module);
module.verbose('Storing instance of visit module', module);
instance = module; instance = module;
$module $module
.data(moduleNamespace, module) .data(moduleNamespace, module)
@ -80,8 +80,8 @@ $.visit = $.fn.visit = function(parameters) {
currentValue = module.get.count(), currentValue = module.get.count(),
newValue = +(currentValue) + 1 newValue = +(currentValue) + 1
; ;
if(id && module.has.visited(id) ) {
module.debug('Unique content already visited, skipping increment', id);
if(id) {
module.add.id(id);
} }
else { else {
if(newValue > settings.limit && !settings.surpass) { if(newValue > settings.limit && !settings.surpass) {
@ -97,8 +97,8 @@ $.visit = $.fn.visit = function(parameters) {
currentValue = module.get.count(), currentValue = module.get.count(),
newValue = +(currentValue) - 1 newValue = +(currentValue) - 1
; ;
if(id && !module.has.visited(id) ) {
module.debug('Decrement skipped, content has not been visited', id);
if(id) {
module.remove.id(id);
} }
else { else {
module.debug('Removing visit'); module.debug('Removing visit');
@ -110,14 +110,20 @@ $.visit = $.fn.visit = function(parameters) {
count: function() { count: function() {
return +(module.retrieve(settings.key.count)) || 0; return +(module.retrieve(settings.key.count)) || 0;
}, },
idCount: function(ids) {
ids = ids || module.get.ids();
return ids.length;
},
ids: function(delimitedIDs) { ids: function(delimitedIDs) {
var
idArray = []
;
delimitedIDs = delimitedIDs || module.retrieve(settings.key.ids); delimitedIDs = delimitedIDs || module.retrieve(settings.key.ids);
if(typeof delimitedIDs === 'string') { if(typeof delimitedIDs === 'string') {
return delimitedIDs.split(settings.delimiter);
}
else {
return [];
idArray = delimitedIDs.split(settings.delimiter);
} }
module.verbose('Found visited ID list', idArray);
return idArray;
}, },
storageOptions: function(data) { storageOptions: function(data) {
var var
@ -137,7 +143,6 @@ $.visit = $.fn.visit = function(parameters) {
}, },
has: { has: {
visited: function(id, ids) { visited: function(id, ids) {
var var
visited = false visited = false
@ -152,7 +157,6 @@ $.visit = $.fn.visit = function(parameters) {
} }
return visited; return visited;
} }
}, },
set: { set: {
@ -178,16 +182,16 @@ $.visit = $.fn.visit = function(parameters) {
: currentIDs + settings.delimiter + id : currentIDs + settings.delimiter + id
; ;
if( module.has.visited(id) ) { if( module.has.visited(id) ) {
module.debug('Unique content already visited, not adding visit', id);
module.debug('Unique content already visited, not adding visit', id, currentIDs);
} }
else if(id === undefined) { else if(id === undefined) {
module.debug('ID is not defined'); module.debug('ID is not defined');
} }
else { else {
module.debug('Adding visit to unique content', id); module.debug('Adding visit to unique content', id);
module.increment(id);
module.store(settings.key.ids, newIDs); module.store(settings.key.ids, newIDs);
} }
module.set.count( module.get.idCount() );
}, },
display: function(selector) { display: function(selector) {
var var
@ -200,7 +204,6 @@ $.visit = $.fn.visit = function(parameters) {
: $element : $element
; ;
} }
module.update.display();
} }
}, },
@ -220,6 +223,22 @@ $.visit = $.fn.visit = function(parameters) {
newIDs = newIDs.join(settings.delimiter); newIDs = newIDs.join(settings.delimiter);
module.store(settings.key.ids, newIDs ); module.store(settings.key.ids, newIDs );
} }
module.set.count( module.get.idCount() );
}
},
check: {
limit: function(value) {
value = value || module.get.count();
if(value >= settings.limit) {
module.debug('Pages viewed exceeded limit, firing callback', value, settings.limit);
$.proxy(settings.onLimit, this)(value);
}
else if(settings.limit) {
module.debug('Limit not reached', value, settings.limit);
}
$.proxy(settings.onChange, this)(value);
module.update.display(value);
} }
}, },
@ -230,11 +249,6 @@ $.visit = $.fn.visit = function(parameters) {
module.debug('Updating displayed view count', $displays); module.debug('Updating displayed view count', $displays);
$displays.html(value); $displays.html(value);
} }
if(value >= settings.limit) {
module.debug('Pages viewed exceeded limit, firing callback', value, settings.limit);
$.proxy(settings.onLimit, this)(value);
}
$.proxy(settings.onChange, this)(value);
} }
}, },
@ -255,7 +269,7 @@ $.visit = $.fn.visit = function(parameters) {
return; return;
} }
if(key == settings.key.count) { if(key == settings.key.count) {
module.update.display(value);
module.check.limit(value);
} }
}, },
retrieve: function(key, value) { retrieve: function(key, value) {
@ -468,7 +482,7 @@ $.fn.visit.settings = {
namespace : 'visit', namespace : 'visit',
increment : true,
increment : false,
surpass : false, surpass : false,
count : false, count : false,
limit : false, limit : false,

Loading…
Cancel
Save