Version 5.1 - All the GraphQL #32
@ -17,19 +17,11 @@
|
|||||||
* This is the config array for javascript files to concatenate and minify
|
* This is the config array for javascript files to concatenate and minify
|
||||||
*/
|
*/
|
||||||
return [
|
return [
|
||||||
/*
|
|
||||||
For each group create an array like so
|
|
||||||
|
|
||||||
'my_group' => array(
|
|
||||||
'path/to/file1.js',
|
|
||||||
'path/to/file2.js'
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
'base' => [
|
'base' => [
|
||||||
//'base/AnimeClient.js',
|
'base/AnimeClient.js',
|
||||||
'base/base.js',
|
//'base/base.js',
|
||||||
'base/event.js',
|
//'base/event.js',
|
||||||
'base/ajax.js',
|
//'base/ajax.js',
|
||||||
],
|
],
|
||||||
'event' => [
|
'event' => [
|
||||||
'base/events.js',
|
'base/events.js',
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
// Action to increment episode count
|
// Action to increment episode count
|
||||||
_.on('body.anime.list', 'click', '.plus_one', function(e) {
|
_.on('body.anime.list', 'click', '.plus_one', function(e) {
|
||||||
let this_sel = this;
|
let this_sel = this;
|
||||||
let parent_sel = this.parentElement;
|
let parent_sel = _.closestParent(this, 'article');
|
||||||
|
|
||||||
let watched_count = parseInt(_.$('.completed_number', parent_sel)[0].textContent, 10);
|
let watched_count = parseInt(_.$('.completed_number', parent_sel)[0].textContent, 10);
|
||||||
let total_count = parseInt(_.$('.total_number', parent_sel)[0].textContent, 10);
|
let total_count = parseInt(_.$('.total_number', parent_sel)[0].textContent, 10);
|
||||||
|
274
public/js/base/AnimeClient.js
Normal file
274
public/js/base/AnimeClient.js
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
var AnimeClient = (function(w) {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const slice = Array.prototype.slice;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ! Base
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function matches(elm, selector) {
|
||||||
|
let matches = (elm.document || elm.ownerDocument).querySelectorAll(selector),
|
||||||
|
i = matches.length;
|
||||||
|
while (--i >= 0 && matches.item(i) !== elm);
|
||||||
|
return i > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _ = {
|
||||||
|
/**
|
||||||
|
* Placeholder function
|
||||||
|
*/
|
||||||
|
noop: () => {},
|
||||||
|
/**
|
||||||
|
* DOM selector
|
||||||
|
*
|
||||||
|
* @param {string} selector - The dom selector string
|
||||||
|
* @param {object} context
|
||||||
|
* @return {array} - array of dom elements
|
||||||
|
*/
|
||||||
|
$(selector, context) {
|
||||||
|
if (typeof selector != "string" || selector === undefined) {
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = (context != null && context.nodeType === 1)
|
||||||
|
? context
|
||||||
|
: document;
|
||||||
|
|
||||||
|
let elements = [];
|
||||||
|
if (selector.match(/^#([\w]+$)/)) {
|
||||||
|
elements.push(document.getElementById(selector.split('#')[1]));
|
||||||
|
} else {
|
||||||
|
elements = slice.apply(context.querySelectorAll(selector));
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Scroll to the top of the Page
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
scrollToTop() {
|
||||||
|
w.scroll(0,0);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Display a message box
|
||||||
|
*
|
||||||
|
* @param {String} type - message type: info, error, success
|
||||||
|
* @param {String} message - the message itself
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
showMessage(type, message) {
|
||||||
|
let template = `
|
||||||
|
<div class="message ${type}">
|
||||||
|
<span class="icon"></span>
|
||||||
|
${message}
|
||||||
|
<span class="close"></span>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
let sel = AnimeClient.$('.message')[0];
|
||||||
|
if (sel !== undefined) {
|
||||||
|
sel.innerHTML = template;
|
||||||
|
sel.removeAttribute('hidden');
|
||||||
|
} else {
|
||||||
|
_.$('header')[0].insertAdjacentHTML('beforeend', template);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Finds the closest parent element matching the passed selector
|
||||||
|
*
|
||||||
|
* @param {DOMElement} current - the current DOMElement
|
||||||
|
* @param {string} parentSelector - selector for the parent element
|
||||||
|
* @return {DOMElement|null} - the parent element
|
||||||
|
*/
|
||||||
|
closestParent(current, parentSelector) {
|
||||||
|
if (Element.prototype.closest !== undefined) {
|
||||||
|
return current.closest(parentSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (current !== document.documentElement) {
|
||||||
|
if (matches(current, parentSelector)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generate a full url from a relative path
|
||||||
|
*
|
||||||
|
* @param {String} path - url path
|
||||||
|
* @return {String} - full url
|
||||||
|
*/
|
||||||
|
url(path) {
|
||||||
|
let uri = `//${document.location.host}`;
|
||||||
|
uri += (path.charAt(0) === '/') ? path : `/${path}`;
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Throttle execution of a function
|
||||||
|
*
|
||||||
|
* @see https://remysharp.com/2010/07/21/throttling-function-calls
|
||||||
|
* @see https://jsfiddle.net/jonathansampson/m7G64/
|
||||||
|
* @param {Number} interval - the minimum throttle time in ms
|
||||||
|
* @param {Function} fn - the function to throttle
|
||||||
|
* @param {Object} scope - the 'this' object for the function
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
throttle(interval, fn, scope) {
|
||||||
|
var wait = false;
|
||||||
|
return function () {
|
||||||
|
var context = scope || this;
|
||||||
|
var args = arguments;
|
||||||
|
|
||||||
|
if ( ! wait) {
|
||||||
|
fn.apply(context, args);
|
||||||
|
wait = true;
|
||||||
|
setTimeout(function() {
|
||||||
|
wait = false;
|
||||||
|
}, interval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ! Events
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function addEvent(sel, event, listener) {
|
||||||
|
// Recurse!
|
||||||
|
if (! event.match(/^([\w\-]+)$/)) {
|
||||||
|
event.split(' ').forEach((evt) => {
|
||||||
|
addEvent(sel, evt, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sel.addEventListener(event, listener, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delegateEvent(sel, target, event, listener) {
|
||||||
|
// Attach the listener to the parent
|
||||||
|
addEvent(sel, event, (e) => {
|
||||||
|
// Get live version of the target selector
|
||||||
|
_.$(target, sel).forEach((element) => {
|
||||||
|
if(e.target == element) {
|
||||||
|
listener.call(element, e);
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_.on = function (sel, event, target, listener) {
|
||||||
|
if (arguments.length === 3) {
|
||||||
|
listener = target;
|
||||||
|
_.$(sel).forEach((el) => {
|
||||||
|
addEvent(el, event, listener);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_.$(sel).forEach((el) => {
|
||||||
|
delegateEvent(el, target, event, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ! Ajax
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url encoding for non-get requests
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @returns {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function ajaxSerialize(data) {
|
||||||
|
let pairs = [];
|
||||||
|
|
||||||
|
Object.keys(data).forEach((name) => {
|
||||||
|
let value = data[name].toString();
|
||||||
|
|
||||||
|
name = encodeURIComponent(name);
|
||||||
|
value = encodeURIComponent(value);
|
||||||
|
|
||||||
|
pairs.push(`${name}=${value}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return pairs.join("&");
|
||||||
|
};
|
||||||
|
|
||||||
|
_.ajax = function(url, config) {
|
||||||
|
// Set some sane defaults
|
||||||
|
config = config || {};
|
||||||
|
config.data = config.data || {};
|
||||||
|
config.type = config.type || 'GET';
|
||||||
|
config.dataType = config.dataType || '';
|
||||||
|
config.success = config.success || _.noop;
|
||||||
|
config.error = config.error || _.noop;
|
||||||
|
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
let method = String(config.type).toUpperCase();
|
||||||
|
|
||||||
|
if (method === "GET") {
|
||||||
|
url += (url.match(/\?/))
|
||||||
|
? ajaxSerialize(config.data)
|
||||||
|
: `?${ajaxSerialize(config.data)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.open(method, url);
|
||||||
|
|
||||||
|
request.onreadystatechange = () => {
|
||||||
|
if (request.readyState === 4) {
|
||||||
|
let responseText = '';
|
||||||
|
|
||||||
|
if (request.responseType == 'json') {
|
||||||
|
responseText = JSON.parse(request.responseText);
|
||||||
|
} else {
|
||||||
|
responseText = request.responseText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.status > 400) {
|
||||||
|
config.error.call(null, request.statusText, request.response);
|
||||||
|
} else {
|
||||||
|
config.success.call(null, responseText, request.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (method) {
|
||||||
|
case "GET":
|
||||||
|
request.send(null);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
request.send(ajaxSerialize(config.data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_.get = function(url, data, callback) {
|
||||||
|
if (arguments.length === 2) {
|
||||||
|
callback = data;
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.ajax(url, {
|
||||||
|
data: data,
|
||||||
|
success: callback
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Export
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return _;
|
||||||
|
})(window);
|
@ -1,89 +0,0 @@
|
|||||||
AnimeClient = (function (ac) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Url encoding for non-get requests
|
|
||||||
*
|
|
||||||
* @param data
|
|
||||||
* @returns {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function serialize(data) {
|
|
||||||
let pairs = [];
|
|
||||||
|
|
||||||
Object.keys(data).forEach((name) => {
|
|
||||||
let value = data[name].toString();
|
|
||||||
|
|
||||||
name = encodeURIComponent(name);
|
|
||||||
value = encodeURIComponent(value);
|
|
||||||
|
|
||||||
pairs.push(`${name}=${value}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
return pairs.join("&");
|
|
||||||
};
|
|
||||||
|
|
||||||
ac.ajax = function(url, config) {
|
|
||||||
// Set some sane defaults
|
|
||||||
config = config || {};
|
|
||||||
config.data = config.data || {};
|
|
||||||
config.type = config.type || 'GET';
|
|
||||||
config.dataType = config.dataType || '';
|
|
||||||
config.success = config.success || ac.noop;
|
|
||||||
config.error = config.error || ac.noop;
|
|
||||||
|
|
||||||
let request = new XMLHttpRequest();
|
|
||||||
let method = String(config.type).toUpperCase();
|
|
||||||
|
|
||||||
if (method === "GET") {
|
|
||||||
url += (url.match(/\?/))
|
|
||||||
? serialize(config.data)
|
|
||||||
: `?${serialize(config.data)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.open(method, url);
|
|
||||||
|
|
||||||
request.onreadystatechange = () => {
|
|
||||||
if (request.readyState === 4) {
|
|
||||||
let responseText = '';
|
|
||||||
|
|
||||||
if (request.responseType == 'json') {
|
|
||||||
responseText = JSON.parse(request.responseText);
|
|
||||||
} else {
|
|
||||||
responseText = request.responseText;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.status > 400) {
|
|
||||||
config.error.call(null, request.statusText, request.response);
|
|
||||||
} else {
|
|
||||||
config.success.call(null, responseText, request.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (method) {
|
|
||||||
case "GET":
|
|
||||||
request.send(null);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
|
||||||
request.send(serialize(config.data));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ac.get = function(url, data, callback) {
|
|
||||||
if (arguments.length === 2) {
|
|
||||||
callback = data;
|
|
||||||
data = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return ac.ajax(url, {
|
|
||||||
data: data,
|
|
||||||
success: callback
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return ac;
|
|
||||||
|
|
||||||
})(AnimeClient);
|
|
@ -1,107 +0,0 @@
|
|||||||
var AnimeClient = (function(w) {
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const slice = Array.prototype.slice;
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Placeholder function
|
|
||||||
*/
|
|
||||||
noop: () => {},
|
|
||||||
/**
|
|
||||||
* DOM selector
|
|
||||||
*
|
|
||||||
* @param {string} selector - The dom selector string
|
|
||||||
* @param {object} context
|
|
||||||
* @return {array} - arrau of dom elements
|
|
||||||
*/
|
|
||||||
$(selector, context) {
|
|
||||||
if (typeof selector != "string" || selector === undefined) {
|
|
||||||
return selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
context = (context != null && context.nodeType === 1)
|
|
||||||
? context
|
|
||||||
: document;
|
|
||||||
|
|
||||||
let elements = [];
|
|
||||||
if (selector.match(/^#([\w]+$)/)) {
|
|
||||||
elements.push(document.getElementById(selector.split('#')[1]));
|
|
||||||
} else {
|
|
||||||
elements = slice.apply(context.querySelectorAll(selector));
|
|
||||||
}
|
|
||||||
|
|
||||||
return elements;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Scroll to the top of the Page
|
|
||||||
*
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
scrollToTop() {
|
|
||||||
w.scroll(0,0);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Display a message box
|
|
||||||
*
|
|
||||||
* @param {String} type - message type: info, error, success
|
|
||||||
* @param {String} message - the message itself
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
showMessage(type, message) {
|
|
||||||
|
|
||||||
let template = `
|
|
||||||
<div class="message ${type}">
|
|
||||||
<span class="icon"></span>
|
|
||||||
${message}
|
|
||||||
<span class="close"></span>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
let sel = AnimeClient.$('.message')[0];
|
|
||||||
if (sel !== undefined) {
|
|
||||||
sel.innerHTML = template;
|
|
||||||
sel.removeAttribute('hidden');
|
|
||||||
} else {
|
|
||||||
AnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Generate a full url from a relative path
|
|
||||||
*
|
|
||||||
* @param {String} path - url path
|
|
||||||
* @return {String} - full url
|
|
||||||
*/
|
|
||||||
url(path) {
|
|
||||||
let uri = `//${document.location.host}`;
|
|
||||||
uri += (path.charAt(0) === '/') ? path : `/${path}`;
|
|
||||||
|
|
||||||
return uri;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Throttle execution of a function
|
|
||||||
*
|
|
||||||
* @see https://remysharp.com/2010/07/21/throttling-function-calls
|
|
||||||
* @see https://jsfiddle.net/jonathansampson/m7G64/
|
|
||||||
* @param {Number} interval - the minimum throttle time in ms
|
|
||||||
* @param {Function} fn - the function to throttle
|
|
||||||
* @param {Object} scope - the 'this' object for the function
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
throttle(interval, fn, scope) {
|
|
||||||
var wait = false;
|
|
||||||
return function () {
|
|
||||||
var context = scope || this;
|
|
||||||
var args = arguments;
|
|
||||||
|
|
||||||
if ( ! wait) {
|
|
||||||
fn.apply(context, args);
|
|
||||||
wait = true;
|
|
||||||
setTimeout(function() {
|
|
||||||
wait = false;
|
|
||||||
}, interval);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})(window);
|
|
@ -1,41 +0,0 @@
|
|||||||
AnimeClient = (function (ac) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function add(sel, event, listener) {
|
|
||||||
// Recurse!
|
|
||||||
if (! event.match(/^([\w\-]+)$/)) {
|
|
||||||
event.split(' ').forEach((evt) => {
|
|
||||||
add(sel, evt, listener);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sel.addEventListener(event, listener, false);
|
|
||||||
}
|
|
||||||
function delegate(sel, target, event, listener) {
|
|
||||||
// Attach the listener to the parent
|
|
||||||
add(sel, event, (e) => {
|
|
||||||
// Get live version of the target selector
|
|
||||||
ac.$(target, sel).forEach((element) => {
|
|
||||||
if(e.target == element) {
|
|
||||||
listener.call(element, e);
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ac.on = function (sel, event, target, listener) {
|
|
||||||
if (arguments.length === 3) {
|
|
||||||
listener = target;
|
|
||||||
ac.$(sel).forEach((el) => {
|
|
||||||
add(el, event, listener);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ac.$(sel).forEach((el) => {
|
|
||||||
delegate(el, target, event, listener);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ac;
|
|
||||||
})(AnimeClient);
|
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
_.on('.manga.list', 'click', '.edit_buttons button', function(e) {
|
_.on('.manga.list', 'click', '.edit_buttons button', function(e) {
|
||||||
let this_sel = this;
|
let this_sel = this;
|
||||||
let parent_sel = this.parentElement.parentElement;
|
let parent_sel = _.closestParent(this, 'article');
|
||||||
let manga_id = parent_sel.id.replace("manga-", "");
|
let manga_id = parent_sel.id.replace("manga-", "");
|
||||||
let type = this_sel.classList.contains("plus_one_chapter") ? 'chapter' : 'volume';
|
let type = this_sel.classList.contains("plus_one_chapter") ? 'chapter' : 'volume';
|
||||||
let completed = parseInt(_.$(`.${type}s_read`, parent_sel)[0].textContent, 10);
|
let completed = parseInt(_.$(`.${type}s_read`, parent_sel)[0].textContent, 10);
|
||||||
|
@ -13,9 +13,7 @@
|
|||||||
<script src="lib/jasmine-2.4.1/boot.js"></script>
|
<script src="lib/jasmine-2.4.1/boot.js"></script>
|
||||||
|
|
||||||
<!-- include source files here... -->
|
<!-- include source files here... -->
|
||||||
<script src="../js/base/base.js"></script>
|
<script src="../js/base/AnimeClient.js"></script>
|
||||||
<script src="../js/base/event.js"></script>
|
|
||||||
<script src="../js/base/ajax.js"></script>
|
|
||||||
|
|
||||||
<!-- include spec files here... -->
|
<!-- include spec files here... -->
|
||||||
<script src="spec/BaseSpec.js"></script>
|
<script src="spec/BaseSpec.js"></script>
|
||||||
|
Loading…
Reference in New Issue
Block a user