CS-Notes/docs/_style/prism-master/utopia.js

463 lines
11 KiB
JavaScript
Raw Normal View History

2018-12-19 14:09:39 +08:00
/**
* Utopia: A JavaScript util library that assumes modern standards support and doesn't fix any browser bugs
* @author Lea Verou (http://lea.verou.me)
* MIT license (http://www.opensource.org/licenses/mit-license.php)
* Last update: 2012-4-29
*/
function $(expr, con) {
return typeof expr === 'string'? (con || document).querySelector(expr) : expr;
}
function $$(expr, con) {
var elements = (con || document).querySelectorAll(expr);
try {
return Array.prototype.slice.call(elements);
}
catch(e) {
var arr = Array(elements.length);
for (var i = elements.length; i-- > 0;) {
arr[i] = elements[i];
}
return arr;
}
}
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, scope) {
for (var i = 0, len = this.length; i < len; ++i) {
fn.call(scope || this, this[i], i, this);
}
}
}
// Make each ID a global variable
// Many browsers do this anyway (its in the HTML5 spec), so it ensures consistency
$$('[id]').forEach(function(element) { window[element.id] = element; });
// Array#splice but for strings
String.prototype.splice = function(i, remove, add) {
remove = +remove || 0;
add = add || '';
return this.slice(0,i) + add + this.slice(i + remove);
};
(function(){
var _ = window.Utopia = {
/**
* Returns the [[Class]] of an object in lowercase (eg. array, date, regexp, string etc)
* Caution: Results for DOM elements and collections aren't reliable.
* @param {Object} obj
*
* @return {String}
*/
type: function(obj) {
if(obj === null) { return 'null'; }
if(obj === undefined) { return 'undefined'; }
var ret = Object.prototype.toString.call(obj).match(/^\[object\s+(.*?)\]$/)[1];
ret = ret? ret.toLowerCase() : '';
if(ret == 'number' && isNaN(obj)) {
return 'NaN';
}
return ret;
},
/**
* Iterate over the properties of an object. Checks whether the properties actually belong to it.
* Can be stopped if the function explicitly returns a value that isn't null, undefined or NaN.
*
* @param obj {Object} The object to iterate over
* @param func {Function} The function used in the iteration. Can accept 2 parameters: one of the
* value of the object and one for its name.
* @param context {Object} Context for the above function. Default is the object being iterated.
*
* @return {Object} Null or the return value of func, if it broke the loop at some point.
*/
each: function(obj, func, context) {
if(!_.type(func) == 'function') {
throw Error('The second argument in Utopia.each() must be a function');
};
context = context || obj;
for (var i in obj) {
if(obj.hasOwnProperty && obj.hasOwnProperty(i)) {
var ret = func.call(context, obj[i], i);
if(!!ret || ret === 0 || ret === '') {
return ret;
}
}
}
return null;
},
/**
* Copies the properties of one object onto another.
* When there is a collision, the later one wins
*
* @return {Object} destination object
*/
merge: function(objects) {
var ret = {};
for(var i=0; i<arguments.length; i++) {
var o = arguments[i];
for(var j in o) {
ret[j] = o[j];
}
}
return ret;
},
/**
* Copies the properties of one or more objects onto the first one
* When there is a collision, the first object wins
*/
attach: function(object, objects) {
for(var i=0; i<arguments.length; i++) {
var o = arguments[i];
for(var j in o) {
if(!(j in object)) {
object[j] = o[j];
}
}
}
return object;
},
element: {
/**
* Creates a new DOM element
* @param options {Object} A set of key/value pairs for attributes, properties, contents, placement in the DOM etc
* @return The new DOM element
*/
create: function() {
var options;
if(_.type(arguments[0]) === 'string') {
if(_.type(arguments[1]) === 'object') {
// Utopia.element.create('div', { ... });
options = arguments[1];
options.tag = arguments[0];
}
else {
// Utopia.element.create('div', ...);
options = {
tag: arguments[0]
};
// Utopia.element.create('div', [contents]);
if(_.type(arguments[1]) === 'array') {
options.contents = arguments[1];
}
// Utopia.element.create('div', 'Text contents');
else if(_.type(arguments[1]) === 'string' || _.type(arguments[1]) === 'number') {
options.contents = ['' + arguments[1]];
}
}
}
else {
options = arguments[0];
}
var namespace = options.namespace || '', element;
if(namespace) {
element = document.createElementNS(namespace, options.tag);
}
else {
element = document.createElement(options.tag);
}
if (options.className || options.id) {
options.properties = options.properties || {};
options.properties.className = options.className;
options.properties.id = options.id;
}
// Set properties, attributes and contents
_.element.set(element, options);
// Place the element in the DOM (inside, before or after an existing element)
// This could be a selector
if(options.before) {
var before = $(options.before);
if (before && before.parentNode) {
before.parentNode.insertBefore(element, before);
}
}
if (options.after && element.parentNode === null) {
var after = $(options.after);
if (after && after.parentNode) {
after.parentNode.insertBefore(element, after.nextSibling)
}
}
if (options.inside && element.parentNode === null) {
$(options.inside).appendChild(element);
}
return element;
},
set: function(element, options) {
_.element.prop(element, options.properties || options.prop);
_.element.attr(element, options.attributes || options.attr);
_.element.contents(element, options.contents);
return element;
},
prop: function (element, properties) {
if (properties) {
for (var prop in properties) {
element[prop] = properties[prop];
}
}
return element;
},
attr: function (element, attributes) {
if (attributes) {
for (attr in attributes) {
element.setAttribute(attr, attributes[attr]);
}
}
return element;
},
/**
* Sets an elements contents
* Contents could be: One or multiple (as an array) of the following:
* - An object literal that will be passed through Utopia.element.create
* - A string or number, which will become a text node
* - An existing DOM element
*/
contents: function (element, contents, where) {
if(contents || contents === 0) {
if (_.type(contents) !== 'array') {
contents = [contents];
}
var firstChild = element.firstChild;
for (var i=0; i<contents.length; i++) {
var content = contents[i], child;
switch(_.type(content)) {
case 'string':
if(content === '') {
continue;
}
// fall through
case 'number':
child = document.createTextNode(content);
break;
case 'object':
child = _.element.create(content);
break;
default:
child = content;
}
if(child) {
if (!where || where === 'end') {
element.appendChild(child);
}
else if (where === 'start') {
element.insertBefore(child, firstChild);
}
}
}
}
return element;
}
},
elements: {
// set, attr, prop, contents functions from Utopia.element, but for multiple elements
},
event: {
/**
* Binds one or more events to one or more elements
*/
bind: function(target, event, callback, traditional) {
if(_.type(target) === 'string' || _.type(target) === 'array') {
var elements = _.type(target) === 'string'? $$(target) : target;
elements.forEach(function(element) {
_.event.bind(element, event, callback, traditional);
});
}
else if(_.type(event) === 'string') {
if(traditional) {
target['on' + event] = callback;
}
else {
target.addEventListener(event, callback, false);
}
}
else if(_.type(event) === 'array') {
for (var i=0; i<event.length; i++) {
_.event.bind(target, event[i], callback, arguments[2]);
}
}
else {
for (var name in event) {
_.event.bind(target, name, event[name], arguments[2]);
}
}
},
/**
* Fire a custom event
*/
fire: function(target, type, properties) {
if(_.type(target) === 'string' || _.type(target) === 'array') {
var elements = _.type(target) === 'string'? $$(target) : target;
elements.forEach(function(element) {
_.event.fire(element, type, properties);
});
}
else if (document.createEvent) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true );
evt.custom = true;
if(properties) {
_.attach(evt, properties);
}
target.dispatchEvent(evt);
}
}
},
/**
* Helper for XHR requests
*/
xhr: function(o) {
document.body.setAttribute('data-loading', '');
var xhr = new XMLHttpRequest(),
method = o.method || 'GET',
data = o.data || '';
xhr.open(method, o.url + (method === 'GET' && data? '?' + data : ''), true);
o.headers = o.headers || {};
if(method !== 'GET' && !o.headers['Content-type'] && !o.headers['Content-Type']) {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
}
for (var header in o.headers) {
xhr.setRequestHeader(header, o.headers[header]);
}
xhr.onreadystatechange = function(){
if(xhr.readyState === 4) {
document.body.removeAttribute('data-loading');
o.callback(xhr);
}
};
xhr.send(method === 'GET'? null : data);
return xhr;
},
/**
* Lazy loads an external script
*/
script: function(url, callback, doc) {
doc = doc || document;
return _.element.create({
tag: 'script',
properties: {
src: url,
async: true,
onload: callback
},
inside: doc.documentElement
});
},
/**
* Returns the absolute X, Y offsets for an element
*/
offset: function(element) {
var left = 0, top = 0, el = element;
if (el.parentNode) {
do {
left += el.offsetLeft;
top += el.offsetTop;
} while ((el = el.offsetParent) && el.nodeType < 9);
el = element;
do {
left -= el.scrollLeft;
top -= el.scrollTop;
} while ((el = el.parentNode) && !/body/i.test(el.nodeName));
}
return {
top: top,
right: innerWidth - left - element.offsetWidth,
bottom: innerHeight - top - element.offsetHeight,
left: left,
};
}
};
['set', 'prop', 'attr', 'contents'].forEach(function(method) {
_.elements[method] = function(elements) {
elements = _.type(elements) === 'string'? $$(elements) : Array.prototype.slice.call(elements);
var args = Array.prototype.slice.call(arguments);
args.shift(); // Remove the elements argument
elements = elements.map(function(element) {
return _.element[method](element, args);
});
return elements;
}
});
})();
window.$u = window.$u || Utopia;