diff --git a/application/config/autoload.php b/application/config/autoload.php new file mode 100644 index 0000000..4750f7d --- /dev/null +++ b/application/config/autoload.php @@ -0,0 +1,116 @@ +meta = ""; + $this->head_js = ""; + $this->foot_js = ""; + $this->css = ""; + $this->title = ""; + $this->head_tags = ""; + $this->body_class = ""; + $this->body_id = ""; + $this->base = ""; + $this->CI =& get_instance(); + + //Define some constants for formatting + define('NL', "\n"); + define('T1', "\t"); + define('T2', T1.T1); + define('T3', T2.T1); + define('T4', T2.T2); + define('T5', T3.T2); + define('T6', T3.T3); + } + + // -------------------------------------------------------------------------- + + /** + * Sets server headers and doctype + * + * Also sets page mime type, based on if sent as + * html or xhtml, and what the target browser + * supports + * + * @param bool $xhtml + * @param bool $html5 + * @return Page + */ + private function _headers($xhtml, $html5) + { + $this->CI->output->set_header("Cache-Control: must-revalidate, public"); + + $this->CI->output->set_header("Vary: Accept"); + $mime = ""; + + //Variable for accept keyword + $accept = (!empty($_SERVER['HTTP_ACCEPT'])) ? $_SERVER['HTTP_ACCEPT'] : ""; + + //Predefine doctype + $doctype_string = ($html5 == TRUE) ? doctype('html5') : doctype('xhtml11'); + + //Predefine charset + $charset = "UTF-8"; + + //If xhtml flag is false, set html4 header + if($xhtml == TRUE) + { + //Check that the user agent accepts application/xhtml+xml, or if it's the W3C Validator + if(stristr($accept,"application/xhtml+xml") || stristr($_SERVER["HTTP_USER_AGENT"],"W3C_Validator")) + { + $mime = "application/xhtml+xml"; + } + //Or if it supports application/xml + else if(stristr($accept,"application/xml")) + { + $mime = "application/xml"; + } + //Or if it supports text/xml + else if(stristr($accept,"text/xml")) + { + $mime = "text/xml"; + } + else //Otherwise, it's tag soup + { + $mime = "text/html"; + + if($html5 == FALSE) //If it's not HTML5, it's HTML4 + { + $doctype_string = doctype('html4-strict'); + } + } + } + else + { + $mime = "text/html"; + + if($html5 == FALSE) + { + $doctype_string = doctype('html4-strict'); + } + } + + // set the doctype according to the mime type which was determined + if($mime == "application/xhtml+xml" || $mime == "text/xml" || $mime == "application/xml") + { + if($html5 == TRUE) + { + $doctype_string = ''; + } + + $doctype_string = "\n" . + $doctype_string . "\n"; + } + else + { + $doctype_string .= "\n"; + } + + // finally, output the mime type and prolog type + $this->CI->output->set_header("Content-Type: $mime;charset=$charset"); + $this->CI->output->set_header("X-UA-Compatible: chrome=1, IE=edge"); + $this->CI->output->set_output($doctype_string); + + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Set Meta + * + * Sets meta tags, with codeigniter native meta tag helper + * + * @param array $meta + * @return Page + */ + public function set_meta($meta) + { + $this->meta .= T1.meta($meta).NL; + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets minified javascript group in header + * @param string $group + * @param bool $debug + * @return Page + */ + public function set_head_js_group($group, $debug=FALSE) + { + if($group === FALSE) + { + return $this; + } + + $file = $this->CI->config->item('group_js_path') . $group; + $file .= ($debug == TRUE) ? "/debug/1" : ""; + $this->head_js .= $this->script_tag($file, FALSE); + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Set an individual js file in header + * @param string $js + * @param bool $domain + * @return Page + */ + public function set_head_js($js, $domain=TRUE) + { + $this->head_js .= $this->script_tag($js, $domain); + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets a minified css group + * @param string $group + * @return Page + */ + public function set_css_group($group) + { + $link = array( + 'href' => $this->CI->config->item('group_style_path') . $group, + 'rel' => 'stylesheet', + 'type' => 'text/css', + ); + $this->css .= T1.link_tag($link).NL; + + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets a minified javascript group for the page footer + * @param string $group + * @return Page + */ + public function set_foot_js_group($group, $debug=FALSE) + { + $file = $this->CI->config->item('group_js_path') . $group; + $file .= ($debug == TRUE) ? "?debug=1" : ""; + $this->foot_js .= $this->script_tag($file, FALSE); + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets js in footer; multiple files are combined and minified. + * @param array $args + * @return Page + */ + public function set_foot_js($js, $domain) + { + $this->foot_js .= $this->script_tag($js, $domain); + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets html title string + * @param string $title + * @return Page + */ + public function set_title($title="") + { + $title = ($title == "") ? + $this->CI->config->item('default_title') : $title; + + $this->title = $title; + + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets custom body class + * @param string $class + * @return Page + */ + public function set_body_class($class="") + { + $this->body_class = $class; + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets custom body id + * @param string $id + * @return Page + */ + public function set_body_id($id="") + { + $this->body_id = $id; + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets custom base href + * @param string href + * @return Page + */ + public function set_base($href) + { + $this->base = $href; + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets custom css tags + * @param string $name + * @param string $media + * @return Page + */ + public function set_css_tag($name, $domain=TRUE, $media="all") + { + $path = $this->CI->config->item('content_domain'); + $css_file = $path . "/css/" . $name . ".css"; + + if ($domain == FALSE) + $css_file = $name; + + $this->css_tags .= T1.link_tag($name, "stylesheet", "text/css", "", $media).NL; + + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets uncompressed js file in footer + * @param string $name + * @param bool $domain + * @return Page + */ + public function set_foot_js_tag($name, $domain=TRUE) + { + $this->foot_js .= $this->script_tag($name, $domain); + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets a custom tag in the header + * @param string $tag + * @return Page + */ + public function set_head_tag($tag) + { + $this->head_tags .= $tag . "\n"; + return $this; + } + + // -------------------------------------------------------------------------- + + /** + * Sets custom page header + * @param mixed $xhtml + * @param bool $html5 + * @param bool $fbml + * @return $this + */ + public function build_header($xhtml = FALSE, $html5 = TRUE) + { + $data = array(); + + //Set Meta Tags + $this->meta = ($html5 == TRUE) ? + T1.''.NL. $this->meta : + T1.meta('content-type', 'text/html; charset=utf-8', 'equiv').NL.$this->meta; + $data['meta'] = $this->meta; + + //Set CSS + if ($this->css != "") + { + $data['css'] = $this->css; + } + else + { + //Set default CSS group + $this->set_css_group($this->CI->config->item('default_css_group')); + $data['css'] = $this->css; + } + + //Set head javascript + if($this->head_js != "") + { + $data['head_js'] = $this->head_js; + } + else + { + $this->set_head_js_group($this->CI->config->item('default_head_js_group')); + $data['head_js'] = $this->head_js; + } + + //Set Page Title + $data['title'] = ($this->title != '') ? $this->title : $this->CI->config->item('default_title'); + + //Set Body Class + $data['body_class'] = $this->body_class; + + //Set Body Id + $data['body_id'] = $this->body_id; + + //Set Base HREF + $data['base'] = $this->base; + + //Set individual head tags + $data['head_tags'] = $this->head_tags; + + //Set Server Headers and Doctype + $this->_headers($xhtml, $html5); + + //Output Header + $this->CI->load->view('header', $data); + + flush(); + + return $this; + } + + // -------------------------------------------------------------------------- + + + /** + * Builds common footer with any additional js + */ + public function build_footer() + { + $data = array(); + + $data['foot_js'] = ($this->foot_js != "") ? + $this->foot_js : ''; + + $this->CI->load->view('footer', $data); + } + + // -------------------------------------------------------------------------- + + /** + * Script Tag + * + * Helper function for making script tags + * + * @param string $js + * @param bool $domain + * @return string + */ + private function script_tag($js, $domain=TRUE) + { + $path = $this->CI->config->item('content_domain'); + $js_file = $path . "/js/" . $js . ".js"; + + if ($domain == FALSE) + $js_file = $js; + + $tag = T1.''.NL; + + return $tag; + } + + // -------------------------------------------------------------------------- + + /** + * Quick Build + * + * A function to make building pages faster + * @param mixed $view + * @param mixed $data + * @param bool $xhtml + * @param bool $html5 + */ + public function quick_build($view, $data, $xhtml=TRUE, $html5=TRUE) + { + //Set up header + if ($title != '') + { + $this->set_title($title); + } + else + { + $this->set_title($this->CI->config->item('default_title')); + } + + $this->build_header($xhtml, $html5); + + //Load view(s) + if (is_array($view)) + { + foreach ($view as $v) + { + $this->CI->load->view($v, $data); + } + } + else + { + $this->CI->load->view($view, $data); + } + + //Create footer + $this->build_footer(); + } + + + // -------------------------------------------------------------------------- + + /** + * Num Queries + * + * Returns number of queries run on a page + * + * @return int + */ + public function num_queries() + { + return (isset($this->CI->db)) ? count($this->CI->db->queries) : 0; + } + + // -------------------------------------------------------------------------- + + /** + * Set Message + * + * Adds a message to the page + * @param string $type + * @param string $message + * @return void + */ + public function set_message($type, $message) + { + $data['stat_class'] = $type; + $data['message'] = $message; + + $this->CI->load->view('message', $data); + } + + // -------------------------------------------------------------------------- + + /** + * Redirect 303 + * + * Shortcut function for 303 redirect + * @param string $url + */ + function redirect_303($url) + { + $this->CI->output->set_header("HTTP/1.1 303 See Other"); + $this->CI->output->set_header("Location:" . $url); + } +} \ No newline at end of file diff --git a/application/views/footer.php b/application/views/footer.php new file mode 100644 index 0000000..23b9ae9 --- /dev/null +++ b/application/views/footer.php @@ -0,0 +1,9 @@ +page->num_queries() ?> + + + \ No newline at end of file diff --git a/application/views/header.php b/application/views/header.php new file mode 100644 index 0000000..3724fe5 --- /dev/null +++ b/application/views/header.php @@ -0,0 +1,11 @@ + + + + + +<?= $title ?> + + + + +> \ No newline at end of file diff --git a/application/views/message.php b/application/views/message.php new file mode 100644 index 0000000..fba8c99 --- /dev/null +++ b/application/views/message.php @@ -0,0 +1,5 @@ +
+ + + +
\ No newline at end of file diff --git a/assets/css/message.css b/assets/css/message.css new file mode 100644 index 0000000..0cb8f8d --- /dev/null +++ b/assets/css/message.css @@ -0,0 +1,36 @@ +.message{ + position:relative; + margin:0.5em auto; + padding:0.5em; + width:95%; +} + +.message .close{ + width:1em; + height:1em; + border:1px solid #000; + position:absolute; + right:0.5em; + top:0.5em; +} + +.message .icon{ + left:0.5em; + top:0.5em; + margin-right:1em; +} + +.error{ + border:1px solid #924949; + background: #f3e6e6; +} + +.success{ + border:1px solid #1f8454; + background: #70dda9; +} + +.info{ + border:1px solid #bfbe3a; + background: #d6d578; +} diff --git a/assets/js/kis-all.js b/assets/js/kis-all.js new file mode 100644 index 0000000..905d650 --- /dev/null +++ b/assets/js/kis-all.js @@ -0,0 +1,926 @@ +/** + Kis JS Keep It Simple JS Library + Copyright Timothy J. Warren + License Public Domain + Version 0.2.0 + */ +(function (){ + + "use strict"; + + //Browser requirements check + if (!document.querySelectorAll) + { + return; + } + + var $_, $, dcopy, sel; + + /** + * $ + * + * Simple DOM selector function + */ + $ = function (a) + { + var x; + if (typeof a !== "string" || typeof a === "undefined"){ return a;} + + //Pick the quickest method for each kind of selector + if(a.match(/^#([\w\-]+$)/)) + { + return document.getElementById(a.split('#')[1]); + } + else if(a.match(/^([\w\-]+)$/)) + { + x = document.getElementsByTagName(a); + } + else + { + x = document.querySelectorAll(a); + } + + //Return the single object if applicable + return (x.length === 1) ? x[0] : x; + }; + + /** + * $_ + * + * Constructor function + */ + $_ = function(s) + { + //Have documentElement be default selector, just in case + if(typeof s == "undefined") + { + sel = (typeof $_.el !== "undefined") + ? $_.el + : document.documentElement; + } + else + { + sel = $(s); + } + + // Make a copy before adding properties + var self = dcopy($_); + + // Give sel to each extension. + for(var i in self) + { + if(typeof self[i] === "object") + { + self[i].el = sel; + } + } + + self.el = sel; + + return self; + }; + + /** + * Deep copy/prototypical constructor function + */ + dcopy = function(obj) + { + var type, F; + + if(typeof obj === "undefined") + { + return; + } + + if(typeof Object.create !== "undefined") + { + return Object.create(obj); + } + + type = typeof obj; + + if(type !== "object" && type !== "function") + { + return; + } + + F = function(){}; + + F.prototype = obj; + + return new F(); + + }; + + //Function to add to $_ object, and get sel + $_.ext = function(name, obj) + { + $_[name] = obj; + obj.el = sel; + }; + + //Selector iteration + $_.ext('each', function (callback) + { + if(typeof sel.length !== "undefined" && sel !== window) + { + var len = sel.length; + + if (len === 0) + { + return; + } + + var selx; + for (var x = 0; x < len; x++) + { + selx = (sel.item(x)) ? sel.item(x) : sel[x]; + callback(selx); + } + } + else + { + callback(sel); + } + }); + + //Set global variables + $_ = window.$_ = window.$_ || $_; + $_.$ = $; + + //console.log polyfill + if(typeof window.console === "undefined") + { + window.console = { + log:function(){} + }; + } + + /** + * String trim function polyfill + */ + if(typeof String.prototype.trim === "undefined") + { + String.prototype.trim = function(){ + return this.replace(/^\s+|\s+$/g, ""); + }; + } + +}()); + +// -------------------------------------------------------------------------- + +//Function to maintain module scope +(function(){ + + "use strict"; + + //Fix $_ is not defined errors + var $_ = $_ || window.$_; + + // -------------------------------------------------------------------------- + + /** + * Dom manipulation object + * + */ + (function (){ + var d; + + /* + * classList.js: Cross-browser full element.classList implementation. + * 2011-06-15 + * + * By Eli Grey, http://eligrey.com + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ + + if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) + { + (function (view){ + + var classListProp = "classList", + protoProp = "prototype", + elemCtrProto = (view.HTMLElement || view.Element)[protoProp], + objCtr = Object, + strTrim = String[protoProp].trim || + function () + { + return this.replace(/^\s+|\s+$/g, ""); + }, + arrIndexOf = Array[protoProp].indexOf || + function (item) + { + var + i = 0, + len = this.length; + for (; i < len; i++) + { + if (i in this && this[i] === item) + { + return i; + } + } + return -1; + } + // Vendors: please allow content code to instantiate DOMExceptions + , + DOMEx = function (type, message) + { + this.name = type; + this.code = DOMException[type]; + this.message = message; + }, + checkTokenAndGetIndex = function (classList, token) + { + if (token === "") + { + throw new DOMEx("SYNTAX_ERR", "An invalid or illegal string was specified"); + } + if (/\s/.test(token)) + { + throw new DOMEx("INVALID_CHARACTER_ERR", "String contains an invalid character"); + } + return arrIndexOf.call(classList, token); + }, + ClassList = function (elem) + { + var + trimmedClasses = strTrim.call(elem.className), + classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [], + i = 0, + len = classes.length; + for (; i < len; i++) + { + this.push(classes[i]); + } + this._updateClassName = function () + { + elem.className = this.toString(); + }; + }, + classListProto = ClassList[protoProp] = [], + classListGetter = function () + { + return new ClassList(this); + }; + // Most DOMException implementations don't allow calling DOMException's toString() + // on non-DOMExceptions. Error's toString() is sufficient here. + DOMEx[protoProp] = Error[protoProp]; + classListProto.item = function (i) + { + return this[i] || null; + }; + classListProto.contains = function (token) + { + token += ""; + return checkTokenAndGetIndex(this, token) !== -1; + }; + classListProto.add = function (token) + { + token += ""; + if (checkTokenAndGetIndex(this, token) === -1) + { + this.push(token); + this._updateClassName(); + } + }; + classListProto.remove = function (token) + { + token += ""; + var index = checkTokenAndGetIndex(this, token); + if (index !== -1) + { + this.splice(index, 1); + this._updateClassName(); + } + }; + classListProto.toggle = function (token) + { + token += ""; + if (checkTokenAndGetIndex(this, token) === -1) + { + this.add(token); + } + else + { + this.remove(token); + } + }; + classListProto.toString = function () + { + return this.join(" "); + }; + + if (objCtr.defineProperty) + { + var classListPropDesc = { + get: classListGetter, + enumerable: true, + configurable: true + }; + try + { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + catch (ex) + { // IE 8 doesn't support enumerable:true + if (ex.number === -0x7FF5EC54) + { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } + } + else if (objCtr[protoProp].__defineGetter__) + { + elemCtrProto.__defineGetter__(classListProp, classListGetter); + } + + }(self)); + } + + // -------------------------------------------------------------------------- + + //Private function for getting/setting attributes + function _attr(sel, name, value) + { + var oldVal, doAttr; + + //Get the value of the attribute, if it exists + if (typeof sel.hasAttribute !== "undefined") + { + if (sel.hasAttribute(name)) + { + oldVal = sel.getAttribute(name); + } + + doAttr = true; + } + else if (typeof sel[name] !== "undefined") + { + oldVal = sel[name]; + doAttr = false; + } + else if (name === "class" && typeof sel.className !== "undefined") //className attribute + { + name = "className"; + oldVal = sel.className; + doAttr = false; + } + + //Well, I guess that attribute doesn't exist + if (typeof oldVal === "undefined" && (typeof value === "undefined" || value === null)) + { + console.log(value); + console.log(sel); + console.log("Element does not have the selected attribute"); + return; + } + + //No value to set? Return the current value + if (typeof value === "undefined") + { + return oldVal; + } + + //Determine what to do with the attribute + if (typeof value !== "undefined" && value !== null) + { + if(doAttr === true) + { + sel.setAttribute(name, value); + } + else + { + sel[name] = value; + } + } + else if (value === null) + { + if(doAttr === true) + { + sel.removeAttribute(name); + } + else + { + delete sel[name]; + } + } + + return (typeof value !== "undefined") ? value : oldVal; + } + + function _toCamel(s) + { + return s.replace(/(\-[a-z])/g, function($1){ + return $1.toUpperCase().replace('-',''); + }); + } + + function _css(sel, prop, val) + { + var equi; + + //Camel-case + prop = _toCamel(prop); + + //Equivalent properties for 'special' browsers + equi = { + outerHeight: "offsetHeight", + outerWidth: "offsetWidth", + top: "posTop" + } + + + //If you don't define a value, try returning the existing value + if(typeof val === "undefined" && sel.style[prop] !== "undefined") + { + return sel.style[prop]; + } + else if(typeof val === "undefined" && sel.style[equi[prop]] !== "undefined") + { + return sel.style[equi[prop]]; + } + + //Let's try the easy way first + if(typeof sel.style[prop] !== "undefined") + { + sel.style[prop] = val; + + //Short circuit + return; + } + else if(sel.style[equi[prop]]) + { + sel.style[equi[prop]] = val; + return; + } + + //No matches? Well, lets log it for now + console.log("Property " + prop + " nor an equivalent seems to exist"); + } + + // -------------------------------------------------------------------------- + + d = { + addClass: function (c) + { + $_.each(function (e){ + e.classList.add(c); + }); + }, + removeClass: function (c) + { + $_.each(function (e){ + e.classList.remove(c); + }); + }, + hide: function () + { + this.css('display', 'none'); + }, + show: function (type) + { + if (typeof type === "undefined") + { + type = "block"; + } + + this.css("display", type); + }, + attr: function (name, value) + { + var sel = this.el; + + //Make sure you don't try to get a bunch of elements + if (sel.length > 1 && typeof value === "undefined") + { + console.log(sel); + console.log("Must be a singular element"); + return; + } + else if (sel.length > 1 && typeof value !== "undefined") //You can set a bunch, though + { + $_.each(function (e){ + return _attr(e, name, value); + }); + } + else //Normal behavior + { + return _attr(sel, name, value); + } + }, + text: function (value) + { + var oldValue, set, type, sel; + + sel = this.el; + + set = (typeof value !== "undefined") ? true : false; + + type = (typeof sel.innerText !== "undefined") + ? "innerText" + : (typeof sel.textContent !== "undefined") + ? "textContent" + : "innerHTML"; + + oldValue = sel[type]; + + if(set) + { + sel[type] = value; + return value; + } + else + { + return oldValue; + } + }, + css: function (prop, val) + { + if(typeof val === "undefined") + { + return _css(this.el, prop); + } + + $_.each(function (e){ + _css(e, prop, val); + }); + } + }; + + $_.ext('dom', d); + + }()); + + // -------------------------------------------------------------------------- + + /** + * Store object + * + * Wrapper for localstorage data serialization + */ + (function (){ + var store = { + get: function (key) + { + return JSON.parse(localStorage.getItem(key)); + }, + set: function (key, value) + { + if (typeof value !== "string") + { + value = JSON.stringify(value); + } + localStorage.setItem(key, value); + }, + getAll: function () + { + var i, + len, + data; + len = localStorage.length; + data = {}; + + for (i = 0; i < len; i++) + { + var name = localStorage.key(i); + var value = localStorage.getItem(name); + data[name] = value; + } + + return data; + } + }; + + $_.ext('store', store); + }()); + + // -------------------------------------------------------------------------- + + /** + * Qs + * + * Object for encoding and decoding querystrings and hashbang strings + */ + (function (){ + + $_.hb = (history.pushState) ? false : true; + + var qs = { + parse: function (hb) + { + hb = hb || $_.hb; + + var h, i, hString, pairs, pLen, data, y; + + data = {}; + + if (hb === true) + { + h = location.hash.split('#!/'); + hString = (h.length > 1) ? h[1] : ''; + } + else if (hb === false || hb === undefined) + { + hString = window.location.search.substring(1); + } + else + { + return false; + } + + pairs = hString.split('&'); + + pLen = pairs.length; + + for (i = 0; i < pLen; i++) + { + y = pairs[i].split('='); + + if (y.length < 2) + { + return data; + } + + data[y[0]] = y[1]; + } + + return data; + }, + set: function (key, value, hb) + { + hb = hb || $_.hb; + var pairs = this.parse(hb); + + if (key !== undefined && value !== undefined) + { + pairs[key] = value; + } + + var vars = []; + + for (var x in pairs) + { + if (pairs.hasOwnProperty(x)) + { + vars.push(x + '=' + pairs[x]); + } + } + + var qs = vars.join('&'); + + if (hb === true) + { + qs = '!/' + qs; + location.hash = qs; + } + + return qs; + }, + get: function (key, hb) + { + hb = hb || $_.hb; + var pairs = this.parse(hb); + return (pairs[key]) ? pairs[key] : ''; + } + }; + + $_.ext('qs', qs); + + }()); + + // -------------------------------------------------------------------------- + + /** + * Ajax + * + * Object for making ajax requests + */ + (function (){ + + var ajax = { + _do: function (url, data, callback, isPost) + { + if (typeof callback === "undefined") + { + callback = function (){}; + } + + var request = (typeof window.XMLHttpRequest !== "undefined") + ? new XMLHttpRequest() + : false; + + var type = (isPost) ? "POST" : "GET"; + + url += (type === "GET") ? "?"+this._serialize(data, true) : ''; + + request.open(type, url); + + request.onreadystatechange = function () + { + if (request.readyState === 4) + { + callback(request.responseText); + } + }; + + if (type === "POST") + { + request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + request.send(this._serialize(data)); + } + else + { + request.send(null); + } + }, + _serialize: function (data, encode) + { + var pairs = []; + + for (var name in data) + { + if (!data.hasOwnProperty(name)) + { + continue; + } + if (typeof data[name] === "function") + { + continue; + } + + var value = data[name].toString(); + + if (encode === true) + { + name = encodeURIComponent(name.replace(" ", "+")); + value = encodeURIComponent(value.replace(" ", "+")); + } + + pairs.push(name + "=" + value); + } + + return pairs.join("&"); + } + }; + + $_.ext('get', function (url, data, callback){ + ajax._do(url, data, callback, false); + }); + + $_.ext('post', function (url, data, callback){ + ajax._do(url, data, callback, true); + }); + }()); + + // -------------------------------------------------------------------------- + + /** + * Event object + * + * Event api wrapper + */ + (function (){ + + // Property name for expandos on DOM objects + var kis_expando = "KIS_0_2_0"; + + var attach, remove, add_remove, e; + + // Define the proper attach and remove functions + // based on browser support + if(typeof document.addEventListener !== "undefined") + { + attach = function (sel, event, callback) + { + if (typeof sel.addEventListener !== "undefined") + { + sel.addEventListener(event, callback, false); + } + }; + remove = function (sel, event, callback) + { + if (typeof sel.removeEventListener !== "undefined") + { + sel.removeEventListener(event, callback, false); + } + }; + } + //typeof function doesn't work in IE where attachEvent is available: brute force it + else if(typeof document.attachEvent !== "undefined") + { + attach = function (sel, event, callback) + { + function listener () { + // Internet Explorer fails to correctly set the 'this' object + // for event listeners, so we need to set it ourselves. + callback.apply(arguments); + } + + if (typeof sel.attachEvent !== "undefined") + { + remove(event, callback); // Make sure we don't have duplicate listeners + + sel.attachEvent("on" + event, listener); + // Store our listener so we can remove it later + var expando = sel[kis_expando] = sel[kis_expando] || {}; + expando.listeners = expando.listeners || {}; + expando.listeners[event] = expando.listeners[event] || []; + expando.listeners[event].push({ + callback: callback, + listener: listener + }); + } + else + { + console.log("Failed to attach event:"+event+" on "+sel); + } + }; + remove = function (sel, event, callback) + { + if(typeof sel.detachEvent !== "undefined") + { + var expando = sel[kis_expando]; + if (expando && expando.listeners + && expando.listeners[event]) + { + var listeners = expando.listeners[event]; + var len = listeners.length; + for (var i=0; i array( + 'path/to/css/file1.css', + 'path/to/css/file2.css' + ), + */ + + 'css' => array( + 'assets/css/message.css', + ), +); \ No newline at end of file diff --git a/config/js_groups.php b/config/js_groups.php new file mode 100644 index 0000000..5fc4020 --- /dev/null +++ b/config/js_groups.php @@ -0,0 +1,16 @@ + array( + 'path/to/css/file1.css', + 'path/to/css/file2.css' + ), + */ + + 'js' => array( + 'assets/js/kis-all.js', + 'assets/js/message.js', + ) + ); \ No newline at end of file diff --git a/config/jshrink.php b/config/jshrink.php new file mode 100644 index 0000000..c9afe4f --- /dev/null +++ b/config/jshrink.php @@ -0,0 +1,333 @@ + nor the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/** + * JShrink + * + * Usage - JShrink::minify($js); + * Usage - JShrink::minify($js, $options); + * Usage - JShrink::minify($js, array('flaggedComments' => false)); + * + * @version 0.2 + * @package JShrink + * @author Robert Hafner + * @license http://www.opensource.org/licenses/bsd-license.php + */ +class JShrink +{ + protected $input; + protected $index = 0; + + protected $a = ''; + protected $b = ''; + protected $c; + + protected $options; + + static protected $defaultOptions = array('flaggedComments' => true); + + static public function minify($js, $options = array()) + { + try{ + $currentOptions = array_merge(self::$defaultOptions, $options); + + ob_start(); + $currentOptions = array_merge(self::$defaultOptions, $options); + $me = new JShrink(); + $me->breakdownScript($js, $currentOptions); + $output = ob_get_clean(); + return $output; + + }catch(Exception $e){ + ob_end_clean(); + throw $e; + } + } + + protected function breakdownScript($js, $currentOptions) + { + $this->options = $currentOptions; + + $js = str_replace("\r\n", "\n", $js); + $this->input = str_replace("\r", "\n", $js); + + $this->a = $this->getReal(); + + // the only time the length can be higher than 1 is if a conditional comment needs to be displayed + // and the only time that can happen for $a is on the very first run + while(strlen($this->a) > 1) + { + echo $this->a; + $this->a = $this->getReal(); + } + + $this->b = $this->getReal(); + + while($this->a !== false && !is_null($this->a) && $this->a !== '') + { + + // now we give $b the same check for conditional comments we gave $a before we began looping + if(strlen($this->b) > 1) + { + echo $this->a . $this->b; + $this->a = $this->getReal(); + $this->b = $this->getReal(); + continue; + } + + switch($this->a) + { + // new lines + case "\n": + // if the next line is something that can't stand alone preserver the newline + if(strpos('(-+{[@', $this->b) !== false) + { + echo $this->a; + $this->saveString(); + break; + } + + // if its a space we move down to the string test below + if($this->b === ' ') + break; + + // otherwise we treat the newline like a space + + case ' ': + if(self::isAlphaNumeric($this->b)) + echo $this->a; + + $this->saveString(); + break; + + default: + switch($this->b) + { + case "\n": + if(strpos('}])+-"\'', $this->a) !== false) + { + echo $this->a; + $this->saveString(); + break; + }else{ + if(self::isAlphaNumeric($this->a)) + { + echo $this->a; + $this->saveString(); + } + } + break; + + case ' ': + if(!self::isAlphaNumeric($this->a)) + break; + + default: + // check for some regex that breaks stuff + if($this->a == '/' && ($this->b == '\'' || $this->b == '"')) + { + $this->saveRegex(); + continue; + } + + echo $this->a; + $this->saveString(); + break; + } + } + + // do reg check of doom + $this->b = $this->getReal(); + + if(($this->b == '/' && strpos('(,=:[!&|?', $this->a) !== false)) + $this->saveRegex(); + } + } + + protected function getChar() + { + if(isset($this->c)) + { + $char = $this->c; + unset($this->c); + }else{ + if(isset($this->input[$this->index])) + { + $char = $this->input[$this->index]; + $this->index++; + }else{ + return false; + } + } + + if($char === "\n" || ord($char) >= 32) + return $char; + + return ' '; + } + + protected function getReal() + { + $startIndex = $this->index; + $char = $this->getChar(); + + if($char == '/') + { + $this->c = $this->getChar(); + + if($this->c == '/') + { + $thirdCommentString = $this->input[$this->index]; + + // kill rest of line + $char = $this->getNext("\n"); + + if($thirdCommentString == '@') + { + $endPoint = ($this->index) - $startIndex; + unset($this->c); + $char = "\n" . substr($this->input, $startIndex, $endPoint);// . "\n"; + }else{ + $char = $this->getChar(); + $char = $this->getChar(); + } + + }elseif($this->c == '*'){ + + $this->getChar(); // current C + $thirdCommentString = $this->getChar(); + + if($thirdCommentString == '@') + { + // we're gonna back up a bit and and send the comment back, where the first + // char will be echoed and the rest will be treated like a string + $this->index = $this->index-2; + return '/'; + + }elseif($this->getNext('*/')){ + // kill everything up to the next */ + + $this->getChar(); // get * + $this->getChar(); // get / + + $char = $this->getChar(); // get next real charactor + + // if YUI-style comments are enabled we reinsert it into the stream + if($this->options['flaggedComments'] && $thirdCommentString == '!') + { + $endPoint = ($this->index - 1) - $startIndex; + echo "\n" . substr($this->input, $startIndex, $endPoint) . "\n"; + } + + }else{ + $char = false; + } + + if($char === false) + throw new JShrinkException('Stray comment. ' . $this->index); + + // if we're here c is part of the comment and therefore tossed + if(isset($this->c)) + unset($this->c); + } + } + return $char; + } + + protected function getNext($string) + { + $pos = strpos($this->input, $string, $this->index); + + if($pos === false) + return false; + + $this->index = $pos ; + return $this->input[$this->index]; + } + + protected function saveString() + { + $this->a = $this->b; + if($this->a == '\'' || $this->a == '"') + { + // save literal string + $stringType = $this->a; + + while(1) + { + echo $this->a; + $this->a = $this->getChar(); + + switch($this->a) + { + case $stringType: + break 2; + + case "\n": + throw new JShrinkException('Unclosed string. ' . $this->index); + break; + + case '\\': + echo $this->a; + $this->a = $this->getChar(); + } + } + } + } + + protected function saveRegex() + { + echo $this->a . $this->b; + + while(($this->a = $this->getChar()) !== false) + { + if($this->a == '/') + break; + + if($this->a == '\\') + { + echo $this->a; + $this->a = $this->getChar(); + } + + if($this->a == "\n") + throw new JShrinkException('Stray regex pattern. ' . $this->index); + + echo $this->a; + } + $this->b = $this->getReal(); + } + + static protected function isAlphaNumeric($char) + { + return preg_match('/^[\w\$]$/', $char) === 1 || $char == '/'; + } + +} + +// Adding a custom exception handler for your own projects just means changing this line +class JShrinkException extends Exception {} +?> \ No newline at end of file diff --git a/css.php b/css.php new file mode 100644 index 0000000..51fd478 --- /dev/null +++ b/css.php @@ -0,0 +1,82 @@ + false)); +} + +$requested_time=(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) + ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) + : time(); + +if($last_modified === $requested_time) +{ + header("HTTP/1.1 304 Not Modified"); + exit(); +} + +header("Content-Type: application/x-javascript; charset=utf8"); +header("Cache-control: public, max-age=691200, must-revalidate"); +header("Last-Modified: ".gmdate('D, d M Y H:i:s', $last_modified)." GMT"); +header("Expires: ".gmdate('D, d M Y H:i:s', (filemtime($base_path.'js.php') + 691200))." GMT"); + +echo $js; + +ob_end_flush(); +//end of js.php