petjs
Version:
Router of ajax applications, HTML5 history API expansion for browsers who not supporting it
816 lines (716 loc) • 39.6 kB
JavaScript
/**
*
* petjs - router of ajax applications, HTML5 history API expansion for browsers who not supporting it
*
* @author Yaroslav Peteychuk <http://www.peteychuk.com/>, <peteychuk@gmail.com>
* @homepage https://github.com/peteychuk/petjs
* @copyright 2012-2014 by Peteychuk
* @version 0.1.3
* @update 03-07-2014
* @includes HistoryAPI 4.0.2 (c) 2011-2013 by Dmitrii Pakhtinov
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
/**
* HistoryAPI for HTML4 browsers
*/
(function(window,True,False,Null,undefined){"use strict";var document=window.document,windowHistory=window.history||{},windowLocation=window.location,api=!!windowHistory.pushState,initialState=api&&windowHistory.state===undefined,initialFire=windowLocation.href,JSON=window.JSON||{},defineProp=Object.defineProperty,defineGetter=Object.prototype.__defineGetter__,defineSetter=Object.prototype.__defineSetter__,historyPushState=windowHistory.pushState,historyReplaceState=windowHistory.replaceState,sessionStorage=window.sessionStorage,hasOwnProperty=Object.prototype.hasOwnProperty,toString=Object.prototype.toString,msie=+((eval("/*@cc_on 1;@*/")&&/msie (\d+)/i.exec(navigator.userAgent)||[])[1]||0),libID=(new Date).getTime(),VBInc=(defineProp||defineGetter)&&(!msie||msie>8)?0:1,iframe=msie<8?document.createElement("iframe"):False,_a,_r,_d,eventPrefix="",addEvent=(_a="addEventListener",window[_a])||(_a="attachEvent",eventPrefix="on",window[_a]),removeEvent=(_r="removeEventListener",window[_r])||(_r="detachEvent",window[_r]),fireEvent=(_d="dispatchEvent",window[_d])||(_d="fireEvent",window[_d]),eventsListPopState=[],eventsListHashChange=[],skipHashChange=0,eventsList={onpopstate:eventsListPopState,popstate:eventsListPopState,onhashchange:eventsListHashChange,hashchange:eventsListHashChange},sets=function(){var e,t,n,r={basepath:"/",redirect:0,type:"/"},i=document.getElementsByTagName("SCRIPT");for(e=0;i[e];e++){if(t=/(.*)\/(?:history|spike)(?:-\d\.\d(?:\.\d)?\w?)?(?:\.min)?.js\?(.*)$/i.exec(i[e].src)||e===i.length-1&&(t=i[e].src.split("?")).length===2&&(t[2]=t[1])&&t){for(e=0,n=t[2].split("&");n[e];){t=n[e++].split("=");r[t[0]]=t[1]=="true"?True:t[1]=="false"?False:t[1]||""}r["basepath"]=r["basepath"]||"/";break}}return r}(),normalizeUrl=function(e){var t,n,r,i,s,o,u,a=new RegExp("^"+sets["basepath"],"i");return function(f,l){if(!f){f=windowLocation.href;if(!api||l){f=windowLocation.protocol+"//"+windowLocation.host+sets["basepath"]+(f.replace(/^[^#]*/,"")||"#").replace(new RegExp("^#[/]?(?:"+sets["type"]+")?"),"")}}else if(!api){var c=normalizeUrl(),h=c._pathname,p=c._protocol;f=/^(?:[a-z]+\:)?\/\//.test(f)?f.indexOf("/")===0?p+f:f:p+"//"+c._host+(f.indexOf("/")===0?f:f.indexOf("?")===0?h+f:f.indexOf("#")===0?h+c._search+f:h.replace(/[^\/]+$/g,"")+f)}if(t!==f){e.href=t=f;o=e.port;s=e.host;u=e.pathname;if(e.protocol==="http:"&&o==80||e.protocol==="https:"&&o==443){s=e.hostname;o=""}u=u.indexOf("/")===0?u:"/"+u;n=u+e.search+e.hash;i=u.replace(a,sets["type"])+e.search;r=i+e.hash}return{_href:e.protocol+"//"+s+n,_protocol:e.protocol,_host:s,_hostname:e.hostname||windowLocation.hostname,_port:o||windowLocation.port,_pathname:u,_search:e.search,_hash:e.hash,_relative:n,_nohash:i,_special:r}}}(document.createElement("a")),History=!VBInc?windowHistory:{back:windowHistory.back,forward:windowHistory.forward,go:windowHistory.go,pushState:Null,replaceState:Null,emulate:!api,toString:function(){return"[object History]"}},HistoryAccessors={state:{get:function(){return iframe&&iframe["storage"]||historyStorage()[History.location.href]||Null}},length:{get:function(){return windowHistory.length}},location:{set:function(e){window.location=e},get:function(){return api?windowLocation:Location}}},Location={assign:function(e){windowLocation.assign(api||e.indexOf("#")!==0?e:"#"+normalizeUrl()._nohash+e)},reload:windowLocation.reload,replace:function(e){windowLocation.replace(api||e.indexOf("#")!==0?e:"#"+normalizeUrl()._nohash+e)},toString:function(){return this.href}},LocationAccessors={href:{set:function(e){windowLocation.href=e},get:function(){return normalizeUrl()._href}},protocol:{set:function(e){windowLocation.protocol=e},get:function(){return windowLocation.protocol}},host:{set:function(e){windowLocation.host=e},get:function(){return windowLocation.host}},hostname:{set:function(e){windowLocation.hostname=e},get:function(){return windowLocation.hostname}},port:{set:function(e){windowLocation.port=e},get:function(){return windowLocation.port}},pathname:{set:function(e){windowLocation.pathname=e},get:function(){return normalizeUrl()._pathname}},search:{set:function(e){windowLocation.search=e},get:function(){return normalizeUrl()._search}},hash:{set:function(e){var t=e.indexOf("#")===0?e:"#"+e,n=normalizeUrl();if(iframe){if(t!=n._hash){History.pushState(Null,Null,n._nohash+t);hashChanged({oldURL:n._href})}}else{windowLocation.hash="#"+n._nohash+t}},get:function(){return normalizeUrl()._hash}}},createStaticObject=function(e,t,n){var r=e,i,s=False;if(defineProp||defineGetter){for(i in t){if(hasOwnProperty.call(t,i)){if(defineGetter){t[i].get&&defineGetter.call(e,i,t[i].get);t[i].set&&defineSetter.call(e,i,t[i].set)}else if(defineProp){try{defineProp(e,i,t[i])}catch(o){if(n){return False}s=True;break}}}}}else s=True;if(s&&VBInc){var u="StaticClass"+libID+VBInc++,a=["Class "+u];if(!("execVB"in window)){execScript("Function execVB(c) ExecuteGlobal(c) End Function","VBScript")}if(!("VBCVal"in window)){execScript("Function VBCVal(o,r) If IsObject(o) Then Set r=o Else r=o End If End Function","VBScript")}for(i in e){a[a.length]="Public ["+i+"]"}if(hasOwnProperty.call(e,"toString")){if(!e.propertyIsEnumerable("toString")){a[a.length]="Public [toString]"}t["(toString)"]={get:function(){return this.toString.call(this)}}}for(i in t){if(hasOwnProperty.call(t,i)){if(t[i].get){e["get "+i]=t[i].get;a.push("Public [get "+i+"]","Public "+(i==="(toString)"?"Default ":"")+"Property Get ["+i+"]","Call VBCVal(me.[get "+i+"].call(me),["+i+"])","End Property")}if(t[i].set){e["set "+i]=t[i].set;a.push("Public [set "+i+"]","Public Property Let ["+i+"](v)","Call me.[set "+i+"].call(me,v)","End Property","Public Property Set ["+i+"](v)","Call me.[set "+i+"].call(me,v)","End Property")}}}a.push("End Class","Function "+u+"Factory()","Set "+u+"Factory=New "+u,"End Function");execVB(a.join("\n"));r=window[u+"Factory"]();for(i in e){r[i]=e[i]}if(hasOwnProperty.call(e,"toString")){r.toString=e.toString}}return r},JSONStringify=JSON.stringify||function(e){function t(e){var t=/[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,n={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};t.lastIndex=0;return t.test(e)?'"'+e.replace(t,function(e){var t=n[e];return typeof t==="string"?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}var n=function(r){var i,s,o,u=(typeof r).charCodeAt(2);return u===114?t(r):u===109?isFinite(r)?String(r):"null":u===111||u===108?String(r):u===106?function(){if(!r)return"null";i=toString.apply(r)==="[object Array]";s=i?"[":"{";if(i){for(o=0;o<r.length;o++){s+=(o==0?"":",")+n(r[o])}}else{for(o in r){if(hasOwnProperty.call(r,o)&&r[o]!==e){s+=(s.length==1?"":",")+t(o)+":"+n(r[o])}}}return s+(i?"]":"}")}():e};return n}(),JSONParse=function(){var e=JSON.parse;return function(t){return t?e?e(t):(new Function("return "+t))():Null}}(),historyStorage=function(e){return sessionStorage?e?sessionStorage.setItem("__hitoryapi__",JSONStringify(e)):JSONParse(sessionStorage.getItem("__hitoryapi__"))||{}:{}},fireStateChange=function(e,t,n){var r=e===2?window.onhashchange:window.onpopstate,i=e===2?"hashchange":"popstate",s,o=eventsList[i];if(document.createEvent){s=document.createEvent("Events");s.initEvent(i,False,False)}else{s=document.createEventObject();s.type=i}s.state=History.state;s.oldURL=t;s.newURL=n;if(r){r.call(window,s)}for(var u=0,a=o.length;u<a;u++){o[u].call(window,s)}},hashChanged=function(){var e=window.onpopstate||Null,t=window.onhashchange||Null,n=0,r=Null,i=normalizeUrl(),s=i._href,o=i._hash.replace(/^#/,""),u=function(){if(initialFire&&!(initialFire=0)&&i._relative!==sets["basepath"]){clearInterval(r);setTimeout(fireStateChange,10)}},a=function(e){var t=normalizeUrl();if(skipHashChange){s=t._href;return skipHashChange=0}var r=e.oldURL||s,i=s=e.newURL||t._href,o=r.replace(/^.*?(#|$)/,""),u=i.replace(/^.*?(#|$)/,"");if(r!=i&&!n){fireStateChange()}n=0;initialFire=0;if(o!=u){fireStateChange(2,r,i)}};addEvent(eventPrefix+"hashchange",a,False);addEvent(eventPrefix+"popstate",function(){if(initialFire===windowLocation.href){return initialFire=0}initialFire=0;fireStateChange(n=1)},False);History["fixURL"]=function(e){return normalizeUrl(e)._relative};History=createStaticObject(History,VBInc?HistoryAccessors:windowHistory.state===undefined?{state:HistoryAccessors.state,location:HistoryAccessors.location}:{location:HistoryAccessors.location});Location=createStaticObject(Location,LocationAccessors);window[_a]=function(e,t,n,r){if(eventsList[e]){eventsList[e].push(t);if(!api&&eventsListPopState===eventsList[e]){u()}}else{addEvent(e,t,n,r)}};window[_r]=function(e,t,n){var r=eventsList[e];if(r){for(var i=r.length;--i;){if(r[i]===t){r.splice(i,1);break}}}else{removeEvent(e,t,n)}};window[_d]=function(e,t){var n=e&&e.type||e,r=eventsList[e],i=r===eventsListPopState?window.onpopstate:window.onhashchange;if(r){t=t||(typeof e=="string"?window.event:e);try{t&&(t.target=window)}catch(s){try{t.srcElement=window}catch(s){}}if(i){i.call(window,t)}for(var o=0,u=r.length;o<u;o++){r[o].call(window,t)}return True}else{return fireEvent(e,t)}};if(VBInc){execScript("Public history, onhashchange","VBScript")}if((!defineProp&&!defineGetter||!createStaticObject(window,{onhashchange:{get:function(){return t},set:function(e){t=e||Null}},onpopstate:{get:function(){return e},set:function(t){if(e=t||Null){!api&&u()}}}},1))&&!api){r=setInterval(function(){if(window.onpopstate){u()}},100)}if(sets["redirect"]&&window.top==window.self){var f=normalizeUrl(Null,True)._relative,l=windowLocation.search,c=windowLocation.pathname,h=sets["basepath"];if(api){if(f!=h&&(new RegExp("^"+h+"$","i")).test(c)){windowLocation.href=f}if(!(new RegExp("^"+h,"i")).test(c)){windowLocation.href=c.replace(/^\//,h)+l}}else if(c!=h){windowLocation.href=h+"#"+c.replace(new RegExp("^"+h,"i"),sets["type"])+l+windowLocation.hash}}return a}();History.pushState=function(e,t,n,r){var i=historyStorage(),s=normalizeUrl()._href,o=n&&normalizeUrl(n);initialFire=0;n=o?o._href:s;if(r&&i[s]){delete i[s]}if((!api||initialState)&&sessionStorage&&e){i[n]=e;historyStorage(i);e=Null}if(historyPushState&&historyReplaceState){if(r){historyReplaceState.call(History,e,t,n)}else{historyPushState.call(History,e,t,n)}}else if(o&&o._relative!=normalizeUrl()._relative){skipHashChange=1;if(r){windowLocation.replace("#"+o._special)}else{windowLocation.hash=o._special}}};History.replaceState=function(e,t,n){History.pushState(e,t,n,1)};if(VBInc){window.history=History;(function(e,t){if(!iframe)return;var n,r,i=function(){var e=normalizeUrl()._href;if(t!=e){hashChanged({oldURL:t,newURL:t=e})}};r=setInterval(i,100);iframe.src="javascript:true;";iframe=document.documentElement.firstChild.appendChild(iframe).contentWindow;History.pushState=n=function(e,s,o,u,a){var f=iframe.document,l=["<script>","lfirst=1;",,"storage="+JSONStringify(e)+";","</script>"],c=o&&normalizeUrl(o);if(!c){iframe["storage"]=e;return}if(!a){clearInterval(r)}if(u){if(iframe["lfirst"]){history.back();n(e,s,c._href,0,1)}else{iframe["storage"]=e;windowLocation.replace("#"+c._special)}}else if(c._href!=t||a){if(!iframe["lfirst"]){iframe["lfirst"]=1;n(iframe["storage"],s,t,0,1)}l[2]='parent.location.hash="'+c._special.replace(/"/g,'\\"')+'";';f.open();f.write(l.join(""));f.close()}if(!a){t=normalizeUrl()._href;r=setInterval(i,100)}};addEvent(eventPrefix+"unload",function(){if(iframe["storage"]){var e={};e[normalizeUrl()._href]=iframe["storage"];document.cookie="_historyAPI="+escape(JSONStringify(e))}clearInterval(r)},False);if(e.length>1){e=unescape(e.pop().split(";").shift());try{iframe["storage"]=JSONParse(e)[normalizeUrl()._href]}catch(s){}}if(!JSON.parse&&!JSON.stringify){JSON.parse=JSONParse;JSON.stringify=JSONStringify;window.JSON=JSON}})(document.cookie.split("_historyAPI="),normalizeUrl()._href)}else{window.history["emulate"]=!api}})(window,true,false,null);
/**
* petjs
*/
(function(window, undefined)
{
"use strict";
/**
*
*
* @type {Object} - pet core (constructor)
* @return {object/function} - public methods
*/
var pet = window.pet = (function()
{
var _public = {}; // this;
var _private = {};
var _pet = _private;
// Options
_private.options = {
debug: false,
basePath: '/',
collection404: false
};
// Local data
_private.data = {
lastQuery: {},
breakRequest: {
lastLocation: '',
breakTo: ''
},
withControlPanel: true
};
/**
* Settings
* @type {Object}
*/
_public.setSettings = function(o)
{
pet.extend(_private.options, o);
};
_public.getSettings = function(key)
{
return _pet.options[key];
};
/**
* Collections
* @type {Object}
*/
_private.collection = {};
/**
* Ready function
* @type {Object}
*/
_private.ready = {
options: {
loaded: false,
readyList: []
},
init: function()
{
var called = false
var ready = function()
{
if (called)
return;
called = true;
// Ready
_pet.ready.loaded();
};
if ( document.addEventListener )
{
document.addEventListener( "DOMContentLoaded", function()
{
ready();
}, false );
}
else if ( document.attachEvent )
{
if ( document.documentElement.doScroll && window == window.top )
{
var tryScroll = function()
{
if (called)
return;
if (!document.body)
return;
try
{
document.documentElement.doScroll("left");
ready();
}
catch(e)
{
setTimeout(tryScroll, 0);
}
};
tryScroll();
}
document.attachEvent("onreadystatechange", function()
{
if ( document.readyState === "complete" )
{
ready();
}
});
}
if (window.addEventListener)
window.addEventListener('load', ready, false);
else if (window.attachEvent)
window.attachEvent('onload', ready);
/* else
window.onload=ready
*/
},
loaded: function()
{
// HTML4 browser ? Set hash location : continue;
if (history.emulate === true)
{
if ( window.location.pathname != (pet.getSettings('basePath')) )
{
window.location.href = pet.getSettings('basePath')+'#'+window.location.pathname;
return;
}
}
// Set settings
_pet.setSettings();
// New fn;
var fn;
pet.ready(function()
{
// Global initialization
_pet.nav.init();
});
_pet.ready.options.loaded = true;
// Execute
for (var i = 0; i < _pet.ready.options.readyList.length; i++)
{
fn = _pet.ready.options.readyList[i];
fn();
}
}
};
_public.ready = function(fn)
{
if (typeof(fn) === 'function')
{
if (_pet.ready.options.loaded)
{
return fn();
}
else
{
_pet.ready.options.readyList.push(fn);
return true;
}
}
return;
};
/**
* Navigation object
* @type {Object}
*/
_private.nav = {
init: function()
{
// Execute ready function for select collection
_pet.router.ready();
// Hang on popstate event triggered by pressing back/forward in browser
window.onpopstate = function( e ) {
// Get location object
var location = window.history.location || window.location;
var loc = location.pathname;
_pet.data.withControlPanel = true;
// Search collection
_pet.router.search(loc, false);
return false;
}
},
setLoc: function(loc, title)
{
history.pushState(null, title, loc); // Push state
}
};
_public.nav = {
go: function(href, event)
{
// OnClick middle button
if (typeof(event) !== 'undefined' && typeof(event.which) !== 'undefined' && event.which === 2)
return true;
var loc;
if ( typeof href === 'object' )
loc = href.href;
else if ( typeof href === 'string' )
loc = href;
else
return true;
if (typeof(event) !== 'undefined')
event.preventDefault();
var urlExp = new RegExp('^(https?\:\/\/'+(window.location.host).replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1")+')','i');
loc = loc.replace(urlExp, '');
if (loc.search(/https?\:\/\//i) === 0)
{
location.href = loc;
return false;
}
_pet.data.withControlPanel = false;
_pet.router.search(loc, true);
if (typeof(event) === 'undefined')
return false;
},
withControlPanel: function()
{
return _pet.data.withControlPanel ? true : false;
}
};
/**
* Router object
* @type {Object}
*/
_private.router = {
options: {},
search: function(loc, replace)
{
// Save last location
_pet.data.breakRequest.lastLocation = pet.router.getLocation();
// Abort previous collection query.
if(_pet.data.lastQuery.finish === false)
{
_pet.data.lastQuery.aborted = true;
_pet.data.lastQuery.finish = true;
_pet.data.lastQuery.abort();
if (typeof(_pet.data.lastQuery.abortOption) === 'function')
_pet.data.lastQuery.abortOption();
// Clear control data
var control = _pet.getControl(_pet.data.breakRequest.breakTo);
_pet.router.options.loc = _pet.data.breakRequest.breakTo;
_pet.router.options.collection = control.collection;
_pet.router.options.param = control.param;
// Save last location
_pet.data.breakRequest.lastLocation = pet.router.getLocation();
}
// Set control data
var control = _pet.getControl(loc);
var collection = control.collection;
var param = control.param;
// Find collection
if (_pet.collection.hasOwnProperty(collection))
{
if (_pet.router.options.collection === collection)
{
if (loc === pet.router.getLocation())
{
//
// Refresh
_pet.collection[collection].refreshLocation();
}
else
{
// changeLocation
var _param = _pet.router.options.param;
var _loc = _pet.router.options.loc;
_pet.collection[collection].beforeTryChangeLocation();
_pet.router.options.param = param;
_pet.router.options.loc = loc;
// Transfer control to the collector
_pet.collection[collection].changeLocation(function(title)
{
// Before internal changeLocation
_pet.router.options.param = _param;
_pet.router.options.loc = _loc;
_pet.collection[collection].beforeChangeLocation();
_pet.router.options.param = param;
_pet.router.options.loc = loc;
// Body function success
_pet.router.success(loc, title, replace);
});
}
}
else
{
var _param = _pet.router.options.param;
var _loc = _pet.router.options.loc;
var _collection = _pet.router.options.collection;
_pet.collection[_collection].beforeTryAway();
_pet.router.options.param = param;
_pet.router.options.loc = loc;
_pet.router.options.collection = collection;
// Transfer control to the collector
_pet.collection[collection].changeLocation(function(title)
{
// Destructor
_pet.router.options.param = _param;
_pet.router.options.loc = _loc;
_pet.router.options.collection = _collection;
_pet.collection[_pet.router.options.collection].__destructor();
// Constructor
_pet.router.options.param = param;
_pet.router.options.loc = loc;
_pet.router.options.collection = collection;
_pet.collection[_pet.router.options.collection].__constructor();
// Body function success
_pet.router.success(loc, title, replace);
});
}
}
else
{
// Show 404 page.
if (_pet.options.collection404 && _pet.collection.hasOwnProperty(_pet.options.collection404))
{
var _param = _pet.router.options.param;
var _loc = _pet.router.options.loc;
var _collection = _pet.router.options.collection;
_pet.collection[_collection].beforeTryAway();
_pet.router.options.param = param;
_pet.router.options.loc = loc;
_pet.router.options.collection = _pet.options.collection404;
_pet.collection[_pet.options.collection404].changeLocation(function(title)
{
// Destructor
_pet.router.options.param = _param;
_pet.router.options.loc = _loc;
_pet.router.options.collection = _collection;
_pet.collection[_pet.router.options.collection].__destructor();
// Constructor
_pet.router.options.param = param;
_pet.router.options.loc = loc;
_pet.router.options.collection = _pet.options.collection404;
_pet.collection[_pet.options.collection404].__constructor();
_pet.router.success(loc, title, replace);
});
}
else
location.href = loc;
}
},
ready: function()
{
if (_pet.collection.hasOwnProperty(_pet.router.options.collection))
_pet.collection[_pet.router.options.collection].ready();
},
success: function(loc, title, replace)
{
// If not history, replaced
if (replace)
_pet.nav.setLoc(loc, title);
document.title = title;
}
};
_public.router = {
getSegment: function(segment, def)
{
return _pet.router.options.param[segment] || def || false;
},
getSegments: function()
{
return _pet.router.options.param;
},
getCollection: function()
{
return _pet.router.options.collection.toString();
},
getLocation: function()
{
return _pet.router.options.loc;
},
getGet: function()
{
// Todo: use cache data
var $_GET = {};
var __GET = window.location.search.substring(1).split("&");
for(var i=0; i<__GET.length; i++)
{
var getVar = __GET[i].split("=");
$_GET[getVar[0]] = typeof(getVar[1])=="undefined" ? "" : getVar[1];
}
return $_GET;
}
};
/**
* Set settings
* @type {function}
*/
_private.setSettings = function()
{
var loc = document.location.pathname;
var control = _pet.getControl(loc);
_pet.router.options.loc = loc;
_pet.router.options.collection = control.collection;
_pet.router.options.param = control.param;
};
/**
* Add collection
* @param name (string) - name of collection
* @param o (object) - options
*/
_public.addCollection = function(name, obj)
{
var def = {
__constructor: function(){},
__destructor: function(){},
ready: function(){},
beforeTryChangeLocation: function(){},
beforeChangeLocation: function(){},
beforeTryAway: function() {},
changeLocation: function(success){success('Pet Framework by Peteychuk aka http://www.peteychuk.com/')},
refreshLocation: function(){}
};
_pet.collection[name] = pet.extend(def, obj);
};
/**
* Get control data
* @param loc {string} - location
* @return {Object} - location data
*/
_private.getControl = function(loc)
{
// Set query. Remove ^/ || /$
var query = loc;
// Cut base path
query = query.replace(new RegExp('^('+_pet.options.basePath+')','gi'), '');
// Cut ^/ /$ .html$
query = query.replace(/^[\/]+/i, '').replace(/[\/]+$/i, '').replace(/\.html?$/i,'');
var collection = 'index'; // Default collection
var param = [];
// Set collection and arguments
if (query.length > 0)
{
var split = query.split('/');
collection = split[0].replace(/^([a-z]+)*(.*)$/i, '$1').toLowerCase() || 'index';
param = split.slice(1, split.length) || [];
}
// Return control data
return {
collection: collection,
param: param
};
};
/**
* Debuger
* @param msg {string} - message
*/
_public.debug = function(msg)
{
if (_pet.options.debug === true)
{
if (window.console && window.console.log)
console.log(msg);
/*else
alert(msg);*/
}
};
/**
* Ajax
* @param o (object) - options
*/
_public.ajax = function(o)
{
return (function()
{
var request;
var postData;
var reConnectTime = 3000;
var type = o.type ? (o.type.toUpperCase()) : 'GET';
var url = o.url || false;
var headers = o.headers || [];
var data = o.data || false;
var dataType = o.dataType || 'txt';
var onError = o.error || function(){};
var onSuccess = o.success || function(){};
var reConnect = o.reConnect || function(){};
// Break request
if (!url)
return false;
// Abort ajax query previous new ajax query
if( o['abort'] && _pet.data.lastQuery.finish === false)
{
_pet.data.lastQuery.aborted = true;
_pet.data.lastQuery.finish = true;
_pet.data.lastQuery.abort();
if (typeof(_pet.data.lastQuery.abortOption) === 'function')
_pet.data.lastQuery.abortOption();
}
// New XMLHTTP
try
{
request = new XMLHttpRequest();
}
catch (e)
{
try
{
request = new ActiveXObject('Msxml2.XMLHTTP');
}
catch (e)
{
request = new ActiveXObject('Microsoft.XMLHTTP');
}
}
try
{
var reConnectFlag = false;
// On ready state change function
request.onreadystatechange = function ()
{
if (request.readyState != 4)
return;
// Error
if ( (request.status != 200) )
{
if ( _pet.data.lastQuery.aborted !== true )
{
if (request.status == 0)
{
reConnectFlag = true;
// ReConnect event (false)
if (o['reConnect'])
reConnect(false);
// Set timer
setTimeout(function()
{
o['reConnect'] = true;
pet.ajax(o);
}, reConnectTime);
}
else
{
// ReConnect event (true)
if (reConnectFlag && o['reConnect'])
reConnect(true);
// Show error
pet.debug('Ajax error status: ' + request.status + '; URL: ' + url);
onError(request, request.status, url);
}
}
return;
}
// ReConnect event (true)
if (reConnectFlag && o['reConnect'])
reConnect(true);
if (o['abort'])
_pet.data.lastQuery.finish = true;
if (dataType == 'json')
onSuccess(_pet.parseJSON(request.responseText));
else
onSuccess(request.responseText);
};
// Open request
request.open(type, url, true);
// Set post data
if (type == 'GET')
{
postData = null;
}
else
{
headers.push(["Content-type", "application/x-www-form-urlencoded; charset=utf-8"]);
postData = data ? _pet.ajaxUrlEncodeData(data) : null;
}
if ( (url.search(/^\//i)!= -1) || (url.search(new RegExp("^https?\:\/\/"+location.host+"\/",'i')) === 0 ) )
headers.push(["X-Requested-With", "XMLHttpRequest"]);
// Set headers
if (headers.length)
for (var i = 0, len = headers.length; i < len; i++)
request.setRequestHeader(headers[i][0], headers[i][1]);
// Send request
request.send(postData);
// Abort options
if (typeof(o['abort']) !== 'undefined')
{
// Set last xhr
_pet.data.lastQuery = request;
_pet.data.lastQuery.finish = false;
_pet.data.lastQuery.abortOption = o['abort'];
_pet.data.breakRequest.breakTo = _pet.data.breakRequest.lastLocation;
}
}
catch (e)
{
if (_private.options.debug)
throw e;
else
pet.debug(e);
};
// Return request object
return request;
})();
};
/**
* Encode data to POST
* @param data
* @return {String}
*/
_private.ajaxUrlEncodeData = function(data)
{
var arr = [];
var str = '';
for(var p in data)
{
if ( (typeof data[p] === 'string') || (typeof data[p] === 'number') || (typeof data[p] === 'boolean') )
{
arr.push((p) + '=' + encodeURIComponent(data[p]));
}
else if ( (data[p] !== null) && data[p].length )
{
str += (str?'&':'')+(urlEncodeData2tree(p, data[p]));
}
}
return (arr.length ? arr.join('&') : arr[0])+(str?'&'+str:'');
};
/**
* Encode data to POST (step 2)
* @param data
* @return {String}
*/
_private.ajaxUrlEncodeDataStep2 = function(key, data)
{
var tmp = [];
for (var k in data)
{
if ( (typeof data[k] === 'string') || (typeof data[k] === 'number') || (typeof data[k] === 'boolean') )
{
tmp.push(key+'['+k+']='+encodeURIComponent(data[k]));
}
else if (data[k].length)
{
tmp.push(urlEncodeData2tree(key+'['+k+']', data[k]));
}
}
if (tmp.length)
return tmp.join('&');
return '';
};
/**
* Parse JSON
* @param {String} - data - JSON
*/
_private.parseJSON = function(data)
{
if ( typeof data !== "string" || !data || data=='' )
return null;
data = data.replace(/^\s+/, '');
data = data.replace(/\s+$/, '');
if (window.JSON && window.JSON.parse)
{
return window.JSON.parse(data);
}
var rvalidchars = /^[\],:{}\s]*$/;
var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g;
if (rvalidchars.test(data.replace(rvalidescape, "@").replace(rvalidtokens, "]").replace(rvalidbraces, "")))
{
return (new Function( "return " + data ))();
}
};
/**
* Extend function
* @param Child {object} - child object
* @param Parent {object} - parent object
*/
_public.extend = function(Child, Parent)
{
var F = function() {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.superclass = Parent.prototype;
for (var key in Parent)
if(Parent.hasOwnProperty(key))
Child[key] = Parent[key];
return Child;
};
/**
*
* @_init (private) - Core initialization
*/
(function()
{
// HTML5 browser ? Remove hash location : continue;
if (history.emulate !== true)
{
if (window.location.hash != "")
{
window.location.href = window.location.hash.replace('#','');
return;
}
}
// Ready initialization
_pet.ready.init();
})();
// Return public methods
return _public;
})();
})(window);