Файл: domAjax.js
Строк: 666
<?php
(function(){
// node.parentElement not node.parentNode to check if it has dom-ajax-repeat
//
this.map = [];
this.version = "1.0.1";
this.mapPointer = 0;
this.useDefaultCallback = true
var walkDom = function()
{
var selector = document.querySelectorAll("[data-ajax-url]");
for(var i = 0; i < selector.length; i++)
{
dom = selector[i];
entry = {};
entry.id = dom.getAttribute("id") || false;
entry.url = dom.getAttribute("data-ajax-url") || false;
entry.method = dom.getAttribute("data-ajax-method") || "get";
entry.event = dom.getAttribute("data-ajax-event") || false;
if(typeof entry.event === "string" && entry.event.indexOf(".") > -1){
ev = entry.event;
entry.event = parseDotValue(ev, 2);
entry.id = parseDotValue(ev, 1);
}
entry.callback = dom.getAttribute("data-ajax-callback") || defaultCallBack;
entry.params = dom.getAttribute("data-ajax-params") || false;
entry.interval = dom.getAttribute("data-ajax-interval") || false;
if(typeof entry.interval === "string" && entry.interval.indexOf(".") > -1){
iv = entry.interval;
entry.interval = parseDotValue(iv, 1);
entry.maxRequests = parseDotValue(iv, 2);
}
this.map[this.map.length] = entry;
delete entry;
// heirarchy, event, interval, instant
}
var selector2 = document.querySelectorAll("[data-ajax-config]");
for(var i = 0; i < selector2.length; i++)
{
dom2 = selector2[i];
config = dom2.getAttribute("data-ajax-config");
if(isValidJson(config))
{
entry = {};
configJson = JSON.parse(config);
entry.id = dom2.getAttribute("id");
entry.url = configJson.url;
entry.method = configJson.method;
entry.event = configJson.event;
if(typeof entry.event === "string" && entry.event.indexOf(".") > -1){
ev = entry.event;
entry.event = parseDotValue(ev, 2);
entry.id = parseDotValue(ev, 1);
}
entry.callback = configJson.callback;
entry.params = configJson.params;
entry.interval = configJson.interval;
if(typeof entry.interval === "string" && entry.interval.indexOf(".") > -1){
iv = entry.interval;
entry.interval = parseDotValue(iv, 1);
entry.maxRequests = parseDotValue(iv, 2);
}
this.map[this.map.length] = entry;
delete entry;
}
else{
console.log("domAjax - error - data-ajax-config does not have valid JSON");
}
}
}
var processDom = function()
{
for(var i = this.mapPointer; i < this.map.length; i++)
{
entry = this.map[i];
if(entry.id)
{
if(entry.event)
{
dom = document.getElementById(entry.id);
if(dom.addEventListener){
dom.addEventListener(entry.event, function(evtObj){
ajax(this.url, this.method, this.params, this.callback, this.id, evtObj);
}.bind(entry));
}
}
else if(entry.interval)
{
intval = entry.interval;
ref = this.map[i];
if(entry.maxRequests){this.map[i].curRequest = 1;}
// comment next line for the first ajax call to occur after interval seconds
// next line makes an instant ajax call before setting up the interval
ajax(ref.url, ref.method, ref.params, ref.callback, ref.id, false);
this.map[i].intervalReference = setInterval(function(ref){
ajax(this.url, this.method, this.params, this.callback, this.id, false);
ref.curRequest++;
if(ref.curRequest > ref.maxRequests){
clearInterval(ref.intervalReference);
}
}.bind(entry), intval, ref);
}
else
{
eid = entry.id;
url = entry.url;
method = entry.method;
params = entry.params;
callback = entry.callback;
ajax(url, method, params, callback, eid, false);
}
}
delete entry;
this.mapPointer = i;
}
this.mapPointer++;
}
var parseConfig = function(config)
{
// expected structure [{id,url,method,event,callback},{},{}]
var toRet = false;
if(config.constructor && config.constructor === Array)
{
for(var i = 0; i < config.length; i++)
{
singleConfig = config[i];
if(singleConfig !== null && typeof singleConfig === 'object')
{
if(singleConfig.event && singleConfig.event.indexOf(".") > -1){
ev = singleConfig.event;
singleConfig.event = parseDotValue(ev, 2);
singleConfig.id = parseDotValue(ev, 1);
}
if(singleConfig.interval && singleConfig.interval.indexOf(".") > -1){
iv = singleConfig.interval;
singleConfig.interval = parseDotValue(iv, 1);
singleConfig.maxRequests = parseDotValue(iv, 2);
}
if(!singleConfig.method){ singleConfig.method = "get"; }
this.map[this.map.length] = singleConfig;
toRet = true;
}
else{console.log("domAjax - error - Parameter in config array is not an object");}
}
}
else{
console.log("domAjax - error - config param passed to domAjax is not an array");
toRet = false;
}
return toRet;
}
var ajax = function(url, method, param, callbackref, entryId, evt)
{
var ajax = new XMLHttpRequest();
ajax.open(method.toUpperCase(), encodeURI(url), true);
//ajax.setRequestHeader('Content-Type', 'application/json');
ajax.onload = function(){
if (ajax.status === 200) {
resp = ajax.responseText;
if(isValidJson(resp))
{
jsn = JSON.parse(resp);
if(jsn.hasOwnProperty("domAjax-redirect"))
{
loc = jsn["domAjax-redirect"]; // todo: decodeUriComponent
location.href = loc;
return;
}
}
else{
tmpJson = {data:ajax.responseText};
resp = JSON.stringify(tmpJson);
}
if(typeof callbackref === "function")
{
if(callbackref === defaultCallBack){
//domAjaxRepeat(JSON.parse(resp));
callbackref(JSON.parse(resp), evt, entryId);
domAjaxRepeat(JSON.parse(resp));
}
else{
callbackref(JSON.parse(resp), evt, entryId);
}
}
else{
var cbref = window[callbackref];
if(typeof cbref === "function")
{
cbref(JSON.parse(resp), evt, entryId);
}
else
{
cbref2 = containsDotAndProcess(callbackref);
if(typeof cbref2 === "function")
{
cbref2(JSON.parse(resp), evt, entryId);
}
else
{
if(this.useDefaultCallback)
{
var errmsg = "domAjax - error - invalid callback for element with ID ";
errmsg += entryId + ". Fallback to default";
console.log(errmsg);
//domAjaxRepeat(JSON.parse(resp));
defaultCallBack(JSON.parse(resp), evt, entryId);
domAjaxRepeat(JSON.parse(resp));
}
else{
var errmsg = "domAjax - error - invalid callback for element with ID ";
errmsg += entryId + ". Default CallBack Disabled";
console.log(errmsg);
}
}
}
}
}
}.bind(this);
if(method.toUpperCase() === "POST" && isValidJson(param)){
ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var arg = "data=" + encodeURIComponent(JSON.stringify(param));
ajax.send(arg);
}
else if(param && isValidJson(param))
{
ajax.setRequestHeader('Content-Type', 'application/json');
ajax.send(JSON.stringify(param));
}
else{
ajax.send();
}
}
var defaultCallBack = function(data, evt, eid)
{
if(typeof eid === "string"){ var elem = document.getElementById(eid); }
else if(typeof eid === "object"){var elem = eid;}
if(elem === null){
console.log("domAjax - error - could not get element with id " + eid);
return;
}
var dataKeys = objKeys(data);
var keyMap = objKeysAsObjArray(data);
var templateKeys = [];
//parseNode();
for(key in dataKeys)
{
var curKey = dataKeys[key];
//var dataValue = parseTemplateStr(curKey, data);
var templateKey = "{{" + curKey + "}}";
templateKeys[templateKeys.length] = templateKey;
//keyMap[templateKey] = dataValue;
//parseNode(elem, dataKeys, keyMap);
}
var textNodes = getTextNodes(elem, templateKeys, true);
for(nkey in textNodes)
{
node = textNodes[nkey];
curText = node.nodeValue;
for(itr in templateKeys)
{
tmpKey = templateKeys[itr];
if(curText.search(tmpKey) !== -1){
var tmpTxt = curText.replace(tmpKey, keyMap[tmpKey]);
curText = tmpTxt;
}
}
node.nodeValue = curText;
}
}
// handle data-ajax-repeat
function domAjaxRepeat(jsonObj){
var domSelector = document.querySelectorAll("[data-ajax-repeat]");
for(var i = 0; i < domSelector.length; i++)
{
dNode = domSelector[i];
dNode.style.display = "none";
// data-ajax-repeat should not be used on domAjax nodes
if(dNode.hasAttribute("data-ajax-url") || dNode.hasAttribute("data-ajax-config"))
{
console.log("domAjax - Warning - data-ajax-repeat should not be used on domAjax nodes");
continue;
}
rootObjString = dNode.getAttribute("data-ajax-repeat") || false;
if(rootObjString)
{
rootObject = parseTemplateStr(rootObjString, jsonObj);
if(Array.isArray(rootObject) && rootObject.length > 0)
{
var c = 1;
for(key in rootObject)
{
currentObj = rootObject[key];
if(typeof currentObj === "object")
{
newNode = dNode.cloneNode(true);
newNode.style.display = "inherit";
newNode.removeAttribute("data-ajax-repeat");
var keyarr = objKeys(currentObj);
var keyobj = objKeysAsObjArray(currentObj);
parseNode(newNode, keyarr, keyobj);
dNode.parentNode.insertBefore(newNode, dNode); // dNode.lastSibling
}
}
}
}
// I can hide node instead of removing it so that dom-ajax-interval
// can function together with dom-ajax-repeat
//dNode.parentNode.removeChild(dNode);
delete dNode;
}
}
function parseNode(node, keyArr, keyArrVal){
// take an arbitrary node, parse any template textnodes in them based on json
var textNodes = getTextNodes(node, keyArr);
parseHTMLinJson(textNodes, keyArr, keyArrVal); //
//var textNodes = getTextNodes(node, keyArr);
for(var nkey in textNodes)
{
node = textNodes[nkey];
curText = node.nodeValue;
for(itr in keyArr)
{
tmpKey = "{{" + keyArr[itr] + "}}";
if(curText.search(tmpKey) !== -1){
txtTemp = curText.replace(tmpKey, keyArrVal[tmpKey]);
curText = txtTemp;
}
}
node.nodeValue = curText;
}
}
// support object dot notation in callback upto 3 properties deep
// obj.obj.obj.method
function containsDotAndProcess(str)
{
if(typeof str === "string")
{
if(str.indexOf(".") > -1){
parts = str.split(".");
if(parts.length < 2){ return false; }
if(parts.length === 2)
{
p1 = parts[0];
p2 = parts[1];
var func = window[p1][p2];
}
else if(parts.length === 3)
{
p1 = parts[0];
p2 = parts[1];
p3 = parts[2];
var func = window[p1][p2][p3];
}
else if(parts.length === 4)
{
p1 = parts[0];
p2 = parts[1];
p3 = parts[2];
p4 = parts[3];
var func = window[p1][p2][p3][p4];
}
if(typeof func === "function"){return func; }
else{return false;}
}
else{return false;}
}else{return false;}
}
function isValidJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
function parseEntry(entry)
{
if(entry.url && entry.url !== ""){}else{ return false; }
if(entry.id && entry.id !== ""){}else{ return false; }
return true;
}
function parseDotValue(dotValue, ret)
{
if(typeof dotValue === "string")
{
if(dotValue.indexOf(".") > -1){
parts = dotValue.split(".");
if(ret === 1){return parts[0]; }
else if(ret === 2){return parts[1];}
}
else{
if(ret === 1){return dotValue; }
else if(ret === 2){return false;}
}
}
}
// recursively walk a json object
function objKeys(obj)
{
var dd = [];
for (var k in obj)
{
if (typeof obj[k] == "object" && obj[k] !== null){
var c = objKeys(obj[k]);
for(var x in c)
{
dd[dd.length] = k + "." + c[x];
}
}
else{
if(typeof obj[k] !== "function"){
dd[dd.length] = k;
}
}
}
return dd;
}
function objKeysAsObjArray(obj, level)
{
var dd = [];
var ff = {};
if(!level){level = 1;}
var l = level + 1;
for (var k in obj)
{
if (typeof obj[k] == "object" && obj[k] !== null){
var c = objKeysAsObjArray(obj[k], l);
for(var x in c)
{
dd[dd.length] = {key:k + "." + c[x].key, val:c[x].val};
}
}
else{
if(typeof obj[k] !== "function"){
dd[dd.length] = {key:k, val:obj[k]};
}
}
}
if(level === 1){
for(var x in dd)
{
var objx = dd[x];
ff["{{" + objx.key + "}}"] = objx.val;
}
return ff;
}
else{
return dd;
}
}
function parseTemplateStr(str, obj)
{
if(typeof str === "string")
{
if(str.indexOf(".") > -1)
{
parts = str.split(".");
if(parts.length === 2){
return obj[parts[0]][parts[1]];
}
if(parts.length === 3){
return obj[parts[0]][parts[1]][parts[2]];
}
if(parts.length === 4){
return obj[parts[0]][parts[1]][parts[2]][parts[3]];
}
}
else if(obj[str]){return obj[str];}
else{return false;}
}
}
function parseHTMLinJson(txtNodes, arrKey, templateMap)
{
for(lKey in txtNodes){
curNode = txtNodes[lKey];
curTxt = curNode.nodeValue;
for(loopArrKey in arrKey){
cKey = "{{" + arrKey[loopArrKey] + "}}";
if(cKey.search("domAjax-html") !== -1){
if(curTxt.search(cKey) !== -1)
{
tmpTxt = curTxt;
thisHtml = templateMap[cKey];
tmpTxt = tmpTxt.replace(cKey, thisHtml);
divNode = document.createElement("div");
divNode.innerHTML = tmpTxt;
while(divNode.firstChild){
curNode.parentNode.insertBefore(divNode.firstChild, curNode);
}
curNode.parentNode.removeChild(curNode);
}
else{ continue; }
}
}
}
}
// traverse dom to get text nodes starting from element
function getTextNodes(elem, props, callingFunction)
{
var textNodes = [];
if(document.createTreeWalker)
{
var treeWalker = document.createTreeWalker(elem, NodeFilter.SHOW_TEXT,
{ acceptNode: function(node) {
if(!props || !Array.isArray(props)){ return NodeFilter.FILTER_SKIP; }
// following conditions should reject node only if the method calling this function is the defaultCallBack
/*
Why I'm Checking parent nodes
To prevent defaultCallBAck from processing {{templates}} within data-ajax-repeat
nodes
*/
try{
if(node.parentNode.hasAttribute("data-ajax-repeat") && callingFunction){
return NodeFilter.FILTER_REJECT;
}
else if(typeof node.parentNode.parentNode !== 'undefined'){
if(node.parentNode.parentNode.hasAttribute("data-ajax-repeat") && callingFunction){
return NodeFilter.FILTER_REJECT;
}
}
else if(typeof node.parentNode.parentNode.parentNode !== 'undefined'){
if(node.parentNode.parentNode.parentNode.hasAttribute("data-ajax-repeat") && callingFunction){
return NodeFilter.FILTER_REJECT;
}
}
}
catch(ex){ return NodeFilter.FILTER_REJECT; }
var nodeText = node.nodeValue;
for(var i in props){
if(nodeText.search(props[i]) !== -1){ return NodeFilter.FILTER_ACCEPT; }
}
return NodeFilter.FILTER_SKIP;
}
});
while(treeWalker.nextNode()){
textNodes.push(treeWalker.currentNode);
}
}
else{
textNodes = getTextNodesIn(elem, props, function(node, parent, prms){
if(!prms || !Array.isArray(prms)){ return false; }
try
{
if(node.parentNode.hasAttribute("data-ajax-repeat")){ return false; }
if(node.parentNode.parentNode)
{
if(node.parentNode.parentNode.hasAttribute("data-ajax-repeat")){
return false; }
}
if(node.parentNode.parentNode.parentNode)
{
if(node.parentNode.parentNode.parentNode.hasAttribute("data-ajax-repeat")){
return false; }
}
}
catch(ex){ return false; }
var nodeText = node.nodeValue;
for(var i in prms){ if(nodeText.search(prms[i])){ return true; }}
return false;
});
}
return textNodes;
}
// http://cwestblog.com/2014/03/14/javascript-getting-all-text-nodes/
// traverse dom to get text nodes starting from element
var getTextNodesIn = function(elem, params, opt_fnFilter) {
var textNodes = [];
if (elem) {
for (var nodes = elem.childNodes, i = nodes.length; i--;) {
var node = nodes[i], nodeType = node.nodeType;
if (nodeType == 3) {
if (!opt_fnFilter || opt_fnFilter(node, elem, params)) {
textNodes.push(node);
}
}
else if (nodeType == 1 || nodeType == 9 || nodeType == 11) {
textNodes = textNodes.concat(getTextNodesIn(node, params, opt_fnFilter));
}
}
}
return textNodes;
}
walkDom();
processDom();
window.domAjax = function(conf, val){
if(typeof conf === "string")
{
switch(conf)
{
case "version":
return this.version;
break;
case "map":
var mp = this.map.slice();
return mp;
break;
// Experimental - Debug - Always true
case "useDefaultCallback":
if(val && typeof val === "boolean")
{
this.useDefaultCallback = val;
}
else{
return this.useDefaultCallback;
}
break;
default:
return this.version;
break;
}
}
else
{
if(parseConfig(conf)){ processDom(); }
}
}.bind(this);
// polyfill for Array.isArray, maybe move to top of script
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
}());
?>