diff --git a/css/lexiconga.css b/css/lexiconga.css index acefd87..167eb5c 100644 --- a/css/lexiconga.css +++ b/css/lexiconga.css @@ -114,7 +114,7 @@ input, textarea, select, option, button { } #loginLink, #logoutLink, -#descriptionToggle, #settingsButton, +#descriptionToggle, #searchFilterToggle, #settingsButton, .deleteCancelButton, .deleteConfirmButton, #settingsScreenCloseButton, #infoScreenCloseButton, .helperlink { diff --git a/css/styles.css b/css/styles.css index 2ca510d..42eebf9 100644 --- a/css/styles.css +++ b/css/styles.css @@ -187,7 +187,9 @@ input[type=checkbox] { cursor: pointer; } -#descriptionToggle { +#descriptionToggle, #searchFilterToggle { + display: inline-block; + margin: 8px; font-weight: bold; font-size: 12px; cursor: pointer; diff --git a/index.php b/index.php index 4da2989..1f54aa4 100644 --- a/index.php +++ b/index.php @@ -159,25 +159,31 @@ elseif (isset($_GET['loggedout']) && $current_user <= 0) { Show Description -
-
@@ -284,7 +290,7 @@ elseif (isset($_GET['loggedout']) && $current_user <= 0) { - + diff --git a/js/defiant-js/defiant-latest.js b/js/defiant-js/defiant-latest.js new file mode 100644 index 0000000..c739d6e --- /dev/null +++ b/js/defiant-js/defiant-latest.js @@ -0,0 +1,687 @@ +/* + * Defiant.js v1.2.5 + * Serch JSON structures plus smart templating with XSLT and XPath. + * http://defiantjs.com + * + * Copyright (c) 2013-2015, Hakan Bilgin + * Licensed under the MIT License + * + * NOTE: + * Robbie Antenesse edited line 165's RegExp to search global and case-insensitive in case of multiple contains() groups. + */ + +if (typeof module === "undefined") { + var module = { exports: undefined }; +} else { + // Node env adaptation goes here... +} + +module.exports = Defiant = (function(window, undefined) { + 'use strict'; + + var Defiant = { + is_ie : /msie/i.test(navigator.userAgent), + is_safari : /safari/i.test(navigator.userAgent), + env : 'production', + xml_decl : '', + namespace : 'xmlns:d="defiant-namespace"', + tabsize : 4, + render: function(template, data) { + var processor = new XSLTProcessor(), + span = document.createElement('span'), + opt = {match: '/'}, + tmpltXpath, + scripts, + temp, + sorter; + // handle arguments + switch (typeof(template)) { + case 'object': + this.extend(opt, template); + if (!opt.data) opt.data = data; + break; + case 'string': + opt.template = template; + opt.data = data; + break; + default: + throw 'error'; + } + opt.data = JSON.toXML(opt.data); + tmpltXpath = '//xsl:template[@name="'+ opt.template +'"]'; + + if (!this.xsl_template) this.gatherTemplates(); + + if (opt.sorter) { + sorter = this.node.selectSingleNode(this.xsl_template, tmpltXpath +'//xsl:for-each//xsl:sort'); + if (sorter) { + if (opt.sorter.order) sorter.setAttribute('order', opt.sorter.order); + if (opt.sorter.select) sorter.setAttribute('select', opt.sorter.select); + sorter.setAttribute('data-type', opt.sorter.type || 'text'); + } + } + + temp = this.node.selectSingleNode(this.xsl_template, tmpltXpath); + temp.setAttribute('match', opt.match); + processor.importStylesheet(this.xsl_template); + span.appendChild(processor.transformToFragment(opt.data, document)); + temp.removeAttribute('match'); + + if (this.is_safari) { + scripts = span.getElementsByTagName('script'); + for (var i=0, il=scripts.length; i'+ str.replace(/defiant:(\w+)/g, '$1') +''); + }, + getSnapshot: function(data) { + return JSON.toXML(data, true); + }, + xmlFromString: function(str) { + var parser, + doc; + str = str.replace(/>\s{1,}<'); + if (str.trim().match(/<\?xml/) === null) { + str = this.xml_decl + str; + } + if (this.is_ie) { + doc = new ActiveXObject('Msxml2.DOMDocument'); + doc.loadXML(str); + if (str.indexOf('xsl:stylesheet') === -1) { + doc.setProperty('SelectionLanguage', 'XPath'); + } + } else { + parser = new DOMParser(); + doc = parser.parseFromString(str, 'text/xml'); + } + return doc; + }, + extend: function(src, dest) { + for (var content in dest) { + if (!src[content] || typeof(dest[content]) !== 'object') { + src[content] = dest[content]; + } else { + this.extend(src[content], dest[content]); + } + } + return src; + }, + node: {} + }; + + return Defiant; + +})(this); + + +if (typeof(XSLTProcessor) === 'undefined') { + + // emulating XSLT Processor (enough to be used in defiant) + var XSLTProcessor = function() {}; + XSLTProcessor.prototype = { + importStylesheet: function(xsldoc) { + this.xsldoc = xsldoc; + }, + transformToFragment: function(data, doc) { + var str = data.transformNode(this.xsldoc), + span = document.createElement('span'); + span.innerHTML = str; + return span; + } + }; + +} + + +// extending STRING +if (!String.prototype.fill) { + String.prototype.fill = function(i,c) { + var str = this; + c = c || ' '; + for (; str.length/, + rx_constructor : /<(.+?)( d:contr=".*?")>/, + rx_namespace : / xmlns\:d="defiant\-namespace"/, + rx_data : /(<.+?>)(.*?)(<\/d:data>)/i, + rx_function : /function (\w+)/i, + to_xml: function(tree) { + var str = this.hash_to_xml(null, tree); + return Defiant.xmlFromString(str); + }, + hash_to_xml: function(name, tree, array_child) { + var is_array = tree.constructor === Array, + elem = [], + attr = [], + key, + val, + val_is_array, + type, + is_attr, + cname, + constr, + cnName, + i; + + for (key in tree) { + val = tree[key]; + if (val === null || val === undefined || val.toString() === 'NaN') val = null; + + is_attr = key.slice(0,1) === '@'; + cname = array_child ? name : key; + if (cname == +cname && tree.constructor !== Object) cname = 'd:item'; + if (val === null) { + constr = null; + cnName = false; + } else { + constr = val.constructor; + cnName = constr.toString().match(this.rx_function)[1]; + } + + if (is_attr) { + attr.push( cname.slice(1) +'="'+ this.escape_xml(val) +'"' ); + if (cnName !== 'String') attr.push( 'd:'+ cname.slice(1) +'="'+ cnName +'"' ); + } else if (val === null) { + elem.push( this.scalar_to_xml( cname, val ) ); + } else { + switch (constr) { + case Function: + // if constructor is function, then it's not a JSON structure + // throw ERROR ? + break; + case Object: + elem.push( this.hash_to_xml( cname, val ) ); + break; + case Array: + if (key === cname) { + val_is_array = val.constructor === Array; + if (val_is_array) { + i = val.length; + while (i--) { + if (val[i].constructor === Array) val_is_array = true; + if (!val_is_array && val[i].constructor === Object) val_is_array = true; + } + } + elem.push( this.scalar_to_xml( cname, val, val_is_array ) ); + break; + } + /* falls through */ + case String: + if (typeof(val) === 'string') { + val = val.toString().replace(/\&/g, '&') + .replace(/\r|\n/g, ' '); + } + if (cname === '#text') { + // prepare map + this.map.push(tree); + attr.push('d:mi="'+ this.map.length +'"'); + attr.push('d:constr="'+ cnName +'"'); + elem.push( this.escape_xml(val) ); + break; + } + /* falls through */ + case Number: + case Boolean: + if (cname === '#text' && cnName !== 'String') { + // prepare map + this.map.push(tree); + attr.push('d:mi="'+ this.map.length +'"'); + attr.push('d:constr="'+ cnName +'"'); + elem.push( this.escape_xml(val) ); + break; + } + elem.push( this.scalar_to_xml( cname, val ) ); + break; + } + } + } + if (!name) { + name = 'd:data'; + attr.push(Defiant.namespace); + if (is_array) attr.push('d:constr="Array"'); + } + if (name.match(this.rx_validate_name) === null) { + attr.push( 'd:name="'+ name +'"' ); + name = 'd:name'; + } + if (array_child) return elem.join(''); + // prepare map + this.map.push(tree); + attr.push('d:mi="'+ this.map.length +'"'); + + return '<'+ name + (attr.length ? ' '+ attr.join(' ') : '') + (elem.length ? '>'+ elem.join('') +'' : '/>' ); + }, + scalar_to_xml: function(name, val, override) { + var attr = '', + text, + constr, + cnName; + + // check whether the nodename is valid + if (name.match(this.rx_validate_name) === null) { + attr += ' d:name="'+ name +'"'; + name = 'd:name'; + override = false; + } + if (val === null || val.toString() === 'NaN') val = null; + if (val === null) return '<'+ name +' d:constr="null"/>'; + if (val.length === 1 && val[0].constructor === Object) { + + text = this.hash_to_xml(false, val[0]); + + var a1 = text.match(this.rx_node), + a2 = text.match(this.rx_constructor); + a1 = (a1 !== null)? a1[2] + .replace(this.rx_namespace, '') + .replace(/>/, '') + .replace(/"\/$/, '"') : ''; + a2 = (a2 !== null)? a2[2] : ''; + + text = text.match(this.rx_data); + text = (text !== null)? text[2] : ''; + + return '<'+ name + a1 +' '+ a2 +' d:type="ArrayItem">'+ text +''; + } else if (val.length === 0 && val.constructor === Array) { + return '<'+ name +' d:constr="Array"/>'; + } + // else + if (override) { + return this.hash_to_xml( name, val, true ); + } + + constr = val.constructor; + cnName = constr.toString().match(this.rx_function)[1]; + text = (constr === Array) ? this.hash_to_xml( 'd:item', val, true ) + : this.escape_xml(val); + + attr += ' d:constr="'+ cnName +'"'; + // prepare map + this.map.push(val); + attr += ' d:mi="'+ this.map.length +'"'; + + return (name === '#text') ? this.escape_xml(val) : '<'+ name + attr +'>'+ text +''; + }, + escape_xml: function(text) { + return String(text) .replace(//g, '>') + .replace(/"/g, '"') + .replace(/ /g, ' '); + } + }, + doc = interpreter.to_xml.call(interpreter, tree); + + // snapshot distinctly improves performance + if (snapshot) { + return { + doc: doc, + src: tree, + map: interpreter.map + }; + } + + this.search.map = interpreter.map; + return doc; + }; +} + +if (!JSON.search) { + JSON.search = function(tree, xpath, single) { + 'use strict'; + + var isSnapshot = tree.doc && tree.doc.nodeType, + doc = isSnapshot ? tree.doc : JSON.toXML(tree), + map = isSnapshot ? tree.map : this.search.map, + src = isSnapshot ? tree.src : tree, + xres = Defiant.node[ single ? 'selectSingleNode' : 'selectNodes' ](doc, xpath.xTransform()), + ret = [], + mapIndex, + i; + + if (single) xres = [xres]; + i = xres.length; + + while (i--) { + switch(xres[i].nodeType) { + case 2: + case 3: + ret.unshift( xres[i].nodeValue ); + break; + default: + mapIndex = +xres[i].getAttribute('d:mi'); + if (map[mapIndex-1]) ret.unshift( map[mapIndex-1] ); + } + } + + // if environment = development, add search tracing + if (Defiant.env === 'development') { + this.trace = JSON.mtrace(src, ret, xres); + } + + return ret; + }; +} + +if (!JSON.mtrace) { + JSON.mtrace = function(root, hits, xres) { + 'use strict'; + + var win = window, + stringify = JSON.stringify, + sroot = stringify( root, null, '\t' ).replace(/\t/g, ''), + trace = [], + i = 0, + il = xres.length, + od = il ? xres[i].ownerDocument.documentElement : false, + map = this.search.map, + hstr, + cConstr, + fIndex = 0, + mIndex, + lStart, + lEnd; + + for (; i 0)? xI[0] : null; + } else { + return XNode.selectSingleNode(XPath); + } +}; + + +Defiant.node.prettyPrint = function(node) { + var root = Defiant, + tabs = root.tabsize, + decl = root.xml_decl.toLowerCase(), + ser, + xstr; + if (root.is_ie) { + xstr = node.xml; + } else { + ser = new XMLSerializer(); + xstr = ser.serializeToString(node); + } + if (root.env !== 'development') { + // if environment is not development, remove defiant related info + xstr = xstr.replace(/ \w+\:d=".*?"| d\:\w+=".*?"/g, ''); + } + var str = xstr.trim().replace(/(>)\s*(<)(\/*)/g, '$1\n$2$3'), + lines = str.split('\n'), + indent = -1, + i = 0, + il = lines.length, + start, + end; + for (; i/g) !== null; + //start = lines[i].match(/<[^\/]+>/g) !== null; + end = lines[i].match(/<\/[\w\:]+>/g) !== null; + if (lines[i].match(/<.*?\/>/g) !== null) start = end = true; + if (start) indent++; + lines[i] = String().fill(indent, '\t') + lines[i]; + if (start && end) indent--; + if (!start && end) indent--; + } + return lines.join('\n').replace(/\t/g, String().fill(tabs, ' ')); +}; + + +Defiant.node.toJSON = function(xnode, stringify) { + 'use strict'; + + var interpret = function(leaf) { + var obj = {}, + win = window, + attr, + type, + item, + cname, + cConstr, + cval, + text, + i, il, a; + + switch (leaf.nodeType) { + case 1: + cConstr = leaf.getAttribute('d:constr'); + if (cConstr === 'Array') obj = []; + else if (cConstr === 'String' && leaf.textContent === '') obj = ''; + + attr = leaf.attributes; + i = 0; + il = attr.length; + for (; i 0) { for (var i = 0; i < currentDictionary.words.length; i++) { if (filter == "" || (filter != "" && currentDictionary.words[i].partOfSpeech == filter)) { - if (search == "" || (search != "" && searchResults.indexOf(htmlEntities(currentDictionary.words[i].name)) >= 0)) { + if (search == "" || (search != "" && (searchByWord || searchBySimple || searchByLong) && searchResults.indexOf(currentDictionary.words[i].wordId) >= 0)) { if (!currentDictionary.words[i].hasOwnProperty("pronunciation")) { currentDictionary.words[i].pronunciation = ""; //Account for new property } @@ -228,27 +243,61 @@ function ShowDictionary() { function DictionaryEntry(itemIndex) { var entryText = "🔗"; - var searchTerm = htmlEntities(document.getElementById("searchBox").value); - var searchRegEx = new RegExp(searchTerm, "gi"); + var searchTerm = document.getElementById("searchBox").value; + var searchByWord = document.getElementById("searchOptionWord").checked; + var searchBySimple = document.getElementById("searchOptionSimple").checked; + var searchByLong = document.getElementById("searchOptionLong").checked; + var searchIgnoreCase = !document.getElementById("searchCaseSensitive").checked; //It's easier to negate case here instead of negating it every use since ignore case is default. + var searchIgnoreDiacritics = document.getElementById("searchIgnoreDiacritics").checked; + + var searchRegEx = new RegExp("(" + ((searchIgnoreDiacritics) ? removeDiacritics(searchTerm) + "|" + searchTerm : searchTerm) + ")", "g" + ((searchIgnoreCase) ? "i" : "")); - entryText += "" + ((searchTerm != "" && document.getElementById("searchOptionWord").checked) ? currentDictionary.words[itemIndex].name.replace(searchRegEx, "" + searchTerm + "") : currentDictionary.words[itemIndex].name) + ""; + entryText += ""; + + if (searchTerm != "" && searchByWord) { + entryText += htmlEntitiesParse(currentDictionary.words[itemIndex].name).replace(searchRegEx, "$1"); + } else { + entryText += currentDictionary.words[itemIndex].name; + } + + entryText += ""; if (currentDictionary.words[itemIndex].pronunciation != "") { - entryText += "" + marked(htmlEntitiesParse(currentDictionary.words[itemIndex].pronunciation)).replace("

","").replace("

","") + "
"; + entryText += ""; + entryText += marked(htmlEntitiesParse(currentDictionary.words[itemIndex].pronunciation)).replace("

","").replace("

",""); + entryText += "
"; } if (currentDictionary.words[itemIndex].partOfSpeech != "") { - entryText += "" + currentDictionary.words[itemIndex].partOfSpeech + ""; + entryText += ""; + entryText += currentDictionary.words[itemIndex].partOfSpeech; + entryText += ""; } entryText += "
"; if (currentDictionary.words[itemIndex].simpleDefinition != "") { - entryText += "" + ((searchTerm != "" && document.getElementById("searchOptionSimple").checked) ? currentDictionary.words[itemIndex].simpleDefinition.replace(searchRegEx, "" + searchTerm + "") : currentDictionary.words[itemIndex].simpleDefinition) + ""; + entryText += ""; + + if (searchTerm != "" && searchBySimple) { + entryText += htmlEntitiesParse(currentDictionary.words[itemIndex].simpleDefinition).replace(searchRegEx, "$1"); + } else { + entryText += currentDictionary.words[itemIndex].simpleDefinition; + } + + entryText += ""; } if (currentDictionary.words[itemIndex].longDefinition != "") { - entryText += "" + ((searchTerm != "" && document.getElementById("searchOptionLong").checked) ? marked(htmlEntitiesParse(currentDictionary.words[itemIndex].longDefinition)).replace(searchRegEx, "" + searchTerm + "") : marked(htmlEntitiesParse(currentDictionary.words[itemIndex].longDefinition))) + ""; + entryText += ""; + + if (searchTerm != "" && searchByLong) { + entryText += marked(htmlEntitiesParse(currentDictionary.words[itemIndex].longDefinition).replace(searchRegEx, "$1")); + } else { + entryText += marked(htmlEntitiesParse(currentDictionary.words[itemIndex].longDefinition)); + } + + entryText += ""; } if (!currentDictionary.settings.isComplete) { @@ -619,6 +668,14 @@ function stripHtmlEntities(string) { return String(string).replace(/&/g, '').replace(/</g, '').replace(/>/g, '').replace(/"/g, '').replace(/'/g, "").replace(/
/g, ''); } +function htmlEntitiesParseForSearchEntry(string) { + return String(string).replace(/"/g, '%%').replace(/'/g, "``"); +} + +function htmlEntitiesParseForSearch(string) { + return String(string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '%%').replace(/'/g, "``"); +} + function dynamicSort(property) { /* Retrieved from http://stackoverflow.com/a/4760279 Usage: theArray.sort(dynamicSort("objectProperty"));*/ diff --git a/js/ui.js b/js/ui.js index c786481..326d812 100644 --- a/js/ui.js +++ b/js/ui.js @@ -155,6 +155,19 @@ function ToggleDescription() { } } +function ToggleSearchFilter() { + var searchFilterToggle = document.getElementById("searchFilterToggle"); + var searchFilterArea = document.getElementById("searchFilterArea"); + + if (searchFilterArea.style.display == "none") { + searchFilterArea.style.display = "block"; + searchFilterToggle.innerHTML = "Hide Search/Filter Options"; + } else { + searchFilterArea.style.display = "none"; + searchFilterToggle.innerHTML = "Search/Filter Options"; + } +} + function ShowInfo(text) { if (text == "terms") { document.getElementById("infoText").innerHTML = termsText;