diff --git a/templates/2k11/admin/index.tpl b/templates/2k11/admin/index.tpl index d2f57374..9302932e 100644 --- a/templates/2k11/admin/index.tpl +++ b/templates/2k11/admin/index.tpl @@ -73,94 +73,96 @@ {$admin_vars.out.footer} {else} {if NOT $admin_vars.no_sidebar} - + {/if}
{$admin_vars.main_content} diff --git a/templates/2k11/admin/serendipity_editor.js.tpl b/templates/2k11/admin/serendipity_editor.js.tpl index 6d2288b6..7a48fcd4 100644 --- a/templates/2k11/admin/serendipity_editor.js.tpl +++ b/templates/2k11/admin/serendipity_editor.js.tpl @@ -551,110 +551,524 @@ window.log = function f(){ log.history = log.history || []; log.history.push(arg }( window.serendipity = window.serendipity || {}, jQuery )); -(function($) { - // Source: https://github.com/yatil/accessifyhtml5.js - var AccessifyHTML5 = function (defaults, more_fixes) { - "use strict"; +// Source: https://github.com/viljamis/responsive-nav.js +var responsiveNav = (function (window, document) { - var fixes = { - 'article' : { 'role': 'article' }, - 'aside' : { 'role': 'complementary' }, - 'nav' : { 'role': 'navigation' }, - 'main' : { 'role': 'main' }, - 'output' : { 'aria-live': 'polite' }, - 'section' : { 'role': 'region' }, - '[required]': { 'aria-required': 'true' } - }, - fix, elems, attr, value, key, obj, i, mo, by_match, el_label, - ATTR_SECURE = /aria-[a-z]+|role|tabindex|title|alt|data-[\w\-]+|lang|style|maxlength|placeholder|pattern|type/, - ID_PREFIX = "acfy-id-", - n_label = 0, - Doc = document; + var computed = !!window.getComputedStyle; - if (Doc.querySelectorAll) { + // getComputedStyle polyfill + if (!window.getComputedStyle) { + window.getComputedStyle = function(el) { + this.el = el; + this.getPropertyValue = function(prop) { + var re = /(\-([a-z]){1})/g; + if (prop === "float") { + prop = "styleFloat"; + } + if (re.test(prop)) { + prop = prop.replace(re, function () { + return arguments[2].toUpperCase(); + }); + } + return el.currentStyle[prop] ? el.currentStyle[prop] : null; + }; + return this; + }; + } - if (defaults) { - if (defaults.header) { - fixes[defaults.header] = { - 'role': 'banner' - }; - } - if (defaults.footer) { - fixes[defaults.footer] = { - 'role': 'contentinfo' - }; - } - if (defaults.main) { - fixes[defaults.main] = { - 'role': 'main' - }; - fixes.main = { - 'role': '' - }; + var nav, + opts, + navToggle, + docEl = document.documentElement, + head = document.getElementsByTagName("head")[0], + styleElement = document.createElement("style"), + navOpen = false, + + // fn arg can be an object or a function, thanks to handleEvent + // read more at: http://www.thecssninja.com/javascript/handleevent + addEvent = function (el, evt, fn, bubble) { + if ("addEventListener" in el) { + // BBOS6 doesn't support handleEvent, catch and polyfill + try { + el.addEventListener(evt, fn, bubble); + } catch (e) { + if (typeof fn === "object" && fn.handleEvent) { + el.addEventListener(evt, function (e) { + // Bind fn as this and set first arg as event object + fn.handleEvent.call(fn, e); + }, bubble); + } else { + throw e; } } + } else if ("attachEvent" in el) { + // check if the callback is an object and contains handleEvent + if (typeof fn === "object" && fn.handleEvent) { + el.attachEvent("on" + evt, function () { + // Bind fn as this + fn.handleEvent.call(fn); + }); + } else { + el.attachEvent("on" + evt, fn); + } + } + }, - for (mo in more_fixes) { - fixes[mo] = more_fixes[mo]; + removeEvent = function (el, evt, fn, bubble) { + if ("removeEventListener" in el) { + try { + el.removeEventListener(evt, fn, bubble); + } catch (e) { + if (typeof fn === "object" && fn.handleEvent) { + el.removeEventListener(evt, function (e) { + fn.handleEvent.call(fn, e); + }, bubble); + } else { + throw e; + } + } + } else if ("detachEvent" in el) { + if (typeof fn === "object" && fn.handleEvent) { + el.detachEvent("on" + evt, function () { + fn.handleEvent.call(fn); + }); + } else { + el.detachEvent("on" + evt, fn); + } + } + }, + + getFirstChild = function (e) { + var firstChild = e.firstChild; + // skip TextNodes + while (firstChild !== null && firstChild.nodeType !== 1) { + firstChild = firstChild.nextSibling; + } + return firstChild; + }, + + setAttributes = function (el, attrs) { + for (var key in attrs) { + el.setAttribute(key, attrs[key]); + } + }, + + addClass = function (el, cls) { + el.className += " " + cls; + el.className = el.className.replace(/(^\s*)|(\s*$)/g,""); + }, + + removeClass = function (el, cls) { + var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)"); + el.className = el.className.replace(reg, " ").replace(/(^\s*)|(\s*$)/g,""); + }, + + ResponsiveNav = function (el, options) { + var i; + + // Default options + this.options = { + animate: true, // Boolean: Use CSS3 transitions, true or false + transition: 400, // Integer: Speed of the transition, in milliseconds + label: "Menu", // String: Label for the navigation toggle + insert: "after", // String: Insert the toggle before or after the navigation + customToggle: "", // Selector: Specify the ID of a custom toggle + openPos: "relative", // String: Position of the opened nav, relative or static + jsClass: "js", // String: 'JS enabled' class which is added to el + init: function() { } ,// Function: Init callback + open: function() { } ,// Function: Open callback + close: function() { } // Function: Close callback + }; + + // User defined options + for (i in options) { + this.options[i] = options[i]; + } + + // Adds "js" class for + addClass(docEl, this.options.jsClass); + + // Wrapper + this.wrapperEl = el.replace("#", ""); + if (document.getElementById(this.wrapperEl)) { + this.wrapper = document.getElementById(this.wrapperEl); + } else { + // If el doesn't exists, stop here. + throw new Error("The nav element you are trying to select doesn't exist"); + } + + // Inner wrapper + this.wrapper.inner = getFirstChild(this.wrapper); + + // For minification + opts = this.options; + nav = this.wrapper; + + // Init + this._init(this); + }; + + ResponsiveNav.prototype = { + // Public methods + destroy: function () { + this._removeStyles(); + removeClass(nav, "closed"); + removeClass(nav, "opened"); + nav.removeAttribute("style"); + nav.removeAttribute("aria-hidden"); + nav = null; + _instance = null; + + removeEvent(window, "load", this, false); + removeEvent(window, "resize", this, false); + removeEvent(navToggle, "mousedown", this, false); + removeEvent(navToggle, "touchstart", this, false); + removeEvent(navToggle, "touchend", this, false); + removeEvent(navToggle, "keyup", this, false); + removeEvent(navToggle, "click", this, false); + + if (!opts.customToggle) { + navToggle.parentNode.removeChild(navToggle); + } else { + navToggle.removeAttribute("aria-hidden"); + } + }, + + toggle: function () { + if (!navOpen) { + removeClass(nav, "closed"); + addClass(nav, "opened"); + nav.style.position = opts.openPos; + setAttributes(nav, { "aria-hidden": "false" }); + + navOpen = true; + opts.open(); + } else { + removeClass(nav, "opened"); + addClass(nav, "closed"); + setAttributes(nav, { "aria-hidden": "true" }); + + if (opts.animate) { + setTimeout(function () { + nav.style.position = "absolute"; + }, opts.transition + 10); + } else { + nav.style.position = "absolute"; } - for (fix in fixes) { - if (fixes.hasOwnProperty(fix)) { + navOpen = false; + opts.close(); + } + }, - elems = Doc.querySelectorAll(fix); - obj = fixes[fix]; + handleEvent: function (e) { + var evt = e || window.event; - for (i = 0; i < elems.length; i++) { + switch (evt.type) { + case "mousedown": + this._onmousedown(evt); + break; + case "touchstart": + this._ontouchstart(evt); + break; + case "touchend": + this._ontouchend(evt); + break; + case "keyup": + this._onkeyup(evt); + break; + case "click": + this._onclick(evt); + break; + case "load": + this._transitions(evt); + this._resize(evt); + break; + case "resize": + this._resize(evt); + break; + } + }, - for (key in obj) { - if (obj.hasOwnProperty(key)) { + // Private methods + _init: function () { + addClass(nav, "closed"); + this._createToggle(); - attr = key; - value = obj[key]; + addEvent(window, "load", this, false); + addEvent(window, "resize", this, false); + addEvent(navToggle, "mousedown", this, false); + addEvent(navToggle, "touchstart", this, false); + addEvent(navToggle, "touchend", this, false); + addEvent(navToggle, "keyup", this, false); + addEvent(navToggle, "click", this, false); + }, - if (!attr.match(ATTR_SECURE)) { - continue; - } - if (!(typeof value).match(/string|number/)) { - continue; - } + _createStyles: function () { + if (!styleElement.parentNode) { + head.appendChild(styleElement); + } + }, - by_match = attr.match(/(describ|label)l?edby/); - if (by_match) { - el_label = Doc.querySelector(value); + _removeStyles: function () { + if (styleElement.parentNode) { + styleElement.parentNode.removeChild(styleElement); + } + }, - if (! el_label) { continue; } + _createToggle: function () { + if (!opts.customToggle) { + var toggle = document.createElement("a"); + toggle.innerHTML = opts.label; + setAttributes(toggle, { + "href": "#", + "id": "nav-toggle" + }); - if (! el_label.id) { - el_label.id = ID_PREFIX + n_label; - } + if (opts.insert === "after") { + nav.parentNode.insertBefore(toggle, nav.nextSibling); + } else { + nav.parentNode.insertBefore(toggle, nav); + } - value = el_label.id; - attr = "aria-" + ("label" === by_match[1] ? "labelledby" : "describedby"); + navToggle = document.getElementById("nav-toggle"); + } else { + var toggleEl = opts.customToggle.replace("#", ""); - n_label++; - } + if (document.getElementById(toggleEl)) { + navToggle = document.getElementById(toggleEl); + } else { + throw new Error("The custom nav toggle you are trying to select doesn't exist"); + } + } + }, - if (!elems[i].hasAttribute(attr)) { - elems[i].setAttribute(attr, value); - } + _preventDefault: function(e) { + if (e.preventDefault) { + e.preventDefault(); + e.stopPropagation(); + } else { + e.returnValue = false; + } + }, + + _onmousedown: function (e) { + var evt = e || window.event; + // If the user isn't right clicking: + if (!(evt.which === 3 || evt.button === 2)) { + this._preventDefault(e); + this.toggle(e); + } + }, + + _ontouchstart: function (e) { + // Touchstart event fires before + // the mousedown and can wipe it + navToggle.onmousedown = null; + this._preventDefault(e); + this.toggle(e); + }, + + _ontouchend: function () { + // Prevents ghost click from happening on some Android browsers + var that = this; + nav.addEventListener("click", that._preventDefault, true); + setTimeout(function () { + nav.removeEventListener("click", that._preventDefault, true); + }, opts.transition); + }, + + _onkeyup: function (e) { + var evt = e || window.event; + if (evt.keyCode === 13) { + this.toggle(e); + } + }, + + _onclick: function (e) { + // For older browsers (looking at IE) + this._preventDefault(e); + }, + + _transitions: function () { + if (opts.animate) { + var objStyle = nav.style, + transition = "max-height " + opts.transition + "ms"; + + objStyle.WebkitTransition = transition; + objStyle.MozTransition = transition; + objStyle.OTransition = transition; + objStyle.transition = transition; + } + }, + + _calcHeight: function () { + var savedHeight = nav.inner.offsetHeight, + innerStyles = "#" + this.wrapperEl + ".opened{ max-height:" + savedHeight + "px }"; + + // Hide from old IE + if (computed) { + styleElement.innerHTML = innerStyles; + innerStyles = ""; + } + }, + + _resize: function () { + if (window.getComputedStyle(navToggle, null).getPropertyValue("display") !== "none") { + setAttributes(navToggle, { "aria-hidden": "false" }); + + // If the navigation is hidden + if (nav.className.match(/(^|\s)closed(\s|$)/)) { + setAttributes(nav, { "aria-hidden": "true" }); + nav.style.position = "absolute"; + } + + this._createStyles(); + this._calcHeight(); + } else { + setAttributes(navToggle, { "aria-hidden": "true" }); + setAttributes(nav, { "aria-hidden": "false" }); + nav.style.position = opts.openPos; + this._removeStyles(); + } + + // Init callback + opts.init(); + } + + }; + + var _instance; + function rn (el, options) { + if (!_instance) { + _instance = new ResponsiveNav(el, options); + } + return _instance; + } + + return rn; +})(window, document); + +// Source: https://github.com/yatil/accessifyhtml5.js +var AccessifyHTML5 = function (defaults, more_fixes) { + "use strict"; + + var fixes = { + 'article' : { 'role': 'article' }, + 'aside' : { 'role': 'complementary' }, + 'nav' : { 'role': 'navigation' }, + 'main' : { 'role': 'main' }, + 'output' : { 'aria-live': 'polite' }, + 'section' : { 'role': 'region' }, + '[required]': { 'aria-required': 'true' } + }, + fix, elems, attr, value, key, obj, i, mo, by_match, el_label, + ATTR_SECURE = /aria-[a-z]+|role|tabindex|title|alt|data-[\w\-]+|lang|style|maxlength|placeholder|pattern|type/, + ID_PREFIX = "acfy-id-", + n_label = 0, + Doc = document; + + if (Doc.querySelectorAll) { + + if (defaults) { + if (defaults.header) { + fixes[defaults.header] = { + 'role': 'banner' + }; + } + if (defaults.footer) { + fixes[defaults.footer] = { + 'role': 'contentinfo' + }; + } + if (defaults.main) { + fixes[defaults.main] = { + 'role': 'main' + }; + fixes.main = { + 'role': '' + }; + } + } + + for (mo in more_fixes) { + fixes[mo] = more_fixes[mo]; + } + + for (fix in fixes) { + if (fixes.hasOwnProperty(fix)) { + + elems = Doc.querySelectorAll(fix); + obj = fixes[fix]; + + for (i = 0; i < elems.length; i++) { + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + + attr = key; + value = obj[key]; + + if (!attr.match(ATTR_SECURE)) { + continue; + } + if (!(typeof value).match(/string|number/)) { + continue; + } + + by_match = attr.match(/(describ|label)l?edby/); + if (by_match) { + el_label = Doc.querySelector(value); + + if (! el_label) { continue; } + + if (! el_label.id) { + el_label.id = ID_PREFIX + n_label; } + + value = el_label.id; + attr = "aria-" + ("label" === by_match[1] ? "labelledby" : "describedby"); + + n_label++; + } + + if (!elems[i].hasAttribute(attr)) { + elems[i].setAttribute(attr, value); } } } } } - }; + } + } +}; - // Accessibility helper script - AccessifyHTML5({ - header: '#top>div>div', - footer: '#meta' +(function($) { + // Fire responsive nav + var navigation = responsiveNav('#main_menu', { + animate: true, // Boolean: Use CSS3 transitions, true or false + transition: 250, // Integer: Speed of the transition, in milliseconds + // String: Label for the navigation toggle + label: " Navigation", + insert: "before", // String: Insert the toggle before or after the navigation + customToggle: "", // Selector: Specify the ID of a custom toggle + openPos: "relative", // String: Position of the opened nav, relative or static + jsClass: "js", // String: 'JS enabled' class which is added to el + debug: false, // Boolean: Log debug messages to console, true or false + init: function() { } , // Function: Init callback + open: function() { } , // Function: Open callback + close: function() { } // Function: Close callback }); // Fire WYSIWYG editor(s) serendipity.spawn(); + // Fire a11y helper script + AccessifyHTML5({ + header: '#top>div>div', + footer: '#meta' + }); + // Editor-area var $entryEditor = $('body').has('#serendipityEntry'); diff --git a/templates/2k11/admin/style.css b/templates/2k11/admin/style.css index 71909dac..fa9da5fd 100644 --- a/templates/2k11/admin/style.css +++ b/templates/2k11/admin/style.css @@ -1220,10 +1220,59 @@ summary { } +/* NAVIGATION + ----------------------------------------------------------------- */ +.smallscreen { + background: #ccdde7; +} + +.js #main_menu { + clip: rect(0 0 0 0); + display: block; + max-height: 0; + overflow: hidden; + position: absolute; + zoom: 1; +} + +.js #main_menu.opened { + max-height: 9999px; +} + +#main_menu h3 { + display: none; + visibility: hidden; +} + +#main_menu > ul { + margin-bottom: .75em; +} + +#main_menu li > ul > li { + float: left; + width: 50%; +} + +#main_menu a { + display: inline-block; + padding: .2125em 0; +} + +#main_menu a, +#main_menu a:visited { + text-decoration: none; +} + +#nav-toggle { + display: inline-block; + font-size: 1.5em; + margin: .25em .5em; +} + + /* CONTENT ----------------------------------------------------------------- */ -#content, -#main_menu { +#content { padding-bottom: 1.5em; } @@ -1428,7 +1477,7 @@ form > .button_link:first-of-type, border-bottom: 1px solid #aaa; font-size: 1em; margin: 0 -1em; - padding: .75em 1em; + padding: .5em 1em; } #dashboard > section { @@ -1865,6 +1914,10 @@ input[name="serendipity[filter][fileCategory]"] { margin: 1.71429em 0; } + #main_menu li > ul > li { + width: 33%; + } + #further_links li { display: inline-block; margin-right: 1em; @@ -2020,9 +2073,54 @@ input[name="serendipity[filter][fileCategory]"] { width: 96%; } + .smallscreen { + background: transparent; + } + + .js #main_menu { + position: relative; + } + + .js #main_menu.closed { + max-height: none; + } + + #main_menu a { + display: block; + padding: .125em 0; + } + + #nav-toggle { + display: none; + } + + #main_menu h3 { + background: #eee; + border-bottom: 1px solid #aaa; + display: block; + font-size: 1em; + margin: 0 -1em; + padding: .5em 1em; + visibility: visible; + } + + #main_menu > ul { + margin-top: 1.5em; + margin-bottom: 1.5em; + } + #main_menu > ul > li { - margin-left: 0; - margin-right: 0; + border: 1px solid #aaa; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 0 0 1em; + padding: 0 1em; + } + + #main_menu li > ul > li { + float: none; + width: 100%; } #edit_entry_timestamp,