";i+=""+a[0].cat+"";i+='
| ";if(!u||l){i+=""}}i+="
';\r\n\r\n html += '' + cat[0].cat + '';\r\n html += '
| '; // col-sm-*\r\n\r\n if (!first || lastCategory) {\r\n html += '
"+codeblock+"\n
";return"\n\n"+codeblock+"\n\n"+nextChar});text=text.replace(/~0/,"");return text}function hashBlock(text){text=text.replace(/(^\n+|\n+$)/g,"");return"\n\n~K"+(g_html_blocks.push(text)-1)+"K\n\n"
+}function _DoCodeSpans(text){text=text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(wholeMatch,m1,m2,m3,m4){var c=m3;c=c.replace(/^([ \t]*)/g,"");c=c.replace(/[ \t]*$/g,"");c=_EncodeCode(c);c=c.replace(/:\/\//g,"~P");return m1+""+c+"
"});return text}function _EncodeCode(text){text=text.replace(/&/g,"&");text=text.replace(//g,">");text=escapeCharacters(text,"*_{}[]\\",false);return text}function _DoItalicsAndBold(text){text=text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g,"$1$3$4");text=text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g,"$1$3$4");return text}function _DoBlockQuotes(text){text=text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(wholeMatch,m1){var bq=m1;bq=bq.replace(/^[ \t]*>[ \t]?/gm,"~0");bq=bq.replace(/~0/g,"");bq=bq.replace(/^[ \t]+$/gm,"");bq=_RunBlockGamut(bq);bq=bq.replace(/(^|\n)/g,"$1 ");bq=bq.replace(/(\s*[^\r]+?<\/pre>)/gm,function(wholeMatch,m1){var pre=m1;pre=pre.replace(/^ /mg,"~0");pre=pre.replace(/~0/g,"");return pre});return hashBlock("\n"+bq+"\n")});return text}function _FormParagraphs(text,doNotUnhash){text=text.replace(/^\n+/g,"");text=text.replace(/\n+$/g,"");var grafs=text.split(/\n{2,}/g);var grafsOut=[];var markerRe=/~K(\d+)K/;var end=grafs.length;for(var i=0;i");str+="";grafsOut.push(str)}}}if(!doNotUnhash){end=grafsOut.length;for(var i=0;i #+-.!])/g,escapeCharacters_callback);return text}var charInsideUrl="[-A-Z0-9+&@#/%?=~_|[\\]()!:,.;]",charEndingUrl="[-A-Z0-9+&@#/%=~_|[\\])]",autoLinkRegex=new RegExp('(="|<)?\\b(https?|ftp)(://'+charInsideUrl+"*"+charEndingUrl+")(?=$|\\W)","gi"),endCharRegex=new RegExp(charEndingUrl,"i");function handleTrailingParens(wholeMatch,lookbehind,protocol,link){if(lookbehind){return wholeMatch}if(link.charAt(link.length-1)!==")"){return"<"+protocol+link+">"}var parens=link.match(/[()]/g);var level=0;for(var i=0;i "+tail}function _DoAutoLinks(text){text=text.replace(autoLinkRegex,handleTrailingParens);var replacer=function(wholematch,m1){return''+pluginHooks.plainLinkText(m1)+""};text=text.replace(/<((https?|ftp):[^'">\s]+)>/gi,replacer);return text}function _UnescapeSpecialChars(text){text=text.replace(/~E(\d+)E/g,function(wholeMatch,m1){var charCodeToReplace=parseInt(m1);return String.fromCharCode(charCodeToReplace)});return text}function _Outdent(text){text=text.replace(/^(\t|[ ]{1,4})/gm,"~0");text=text.replace(/~0/g,"");return text}function _Detab(text){if(!/\t/.test(text)){return text}var spaces=[" "," "," "," "],skew=0,v;return text.replace(/[\n\t]/g,function(match,offset){if(match==="\n"){skew=offset+1;return match}v=(offset-skew)%4;skew=offset+1;return spaces[v]})}var _problemUrlChars=/(?:["'*()[\]:]|~D)/g;function encodeProblemUrlChars(url){if(!url){return""}var len=url.length;return url.replace(_problemUrlChars,function(match,offset){if(match=="~D"){return"%24"}if(match==":"){return":"}return"%"+match.charCodeAt(0).toString(16)})}function escapeCharacters(text,charsToEscape,afterBackslash){var regexString="(["+charsToEscape.replace(/([\[\]\\])/g,"\\$1")+"])";if(afterBackslash){regexString="\\\\"+regexString}var regex=new RegExp(regexString,"g");text=text.replace(regex,escapeCharacters_callback);return text}function escapeCharacters_callback(wholeMatch,m1){var charCodeToEscape=m1.charCodeAt(0);return"~E"+charCodeToEscape+"E"}}})(); + +// Markdown.Extra.js +(function(){var inlineTags=new RegExp(["^(<\\/?(a|abbr|acronym|applet|area|b|basefont|","bdo|big|button|cite|code|del|dfn|em|figcaption|","font|i|iframe|img|input|ins|kbd|label|map|","mark|meter|object|param|progress|q|ruby|rp|rt|s|","samp|script|select|small|span|strike|strong|","sub|sup|textarea|time|tt|u|var|wbr)[^>]*>|","<(br)\\s?\\/?>)$"].join(""),"i");if(!Array.indexOf){Array.prototype.indexOf=function(obj){for(var i=0;i ]*>?/gi,function(tag){return tag.match(whitelist)?tag:""})}function union(x,y){var obj={};for(var i=0;i ~X"+(this.hashBlocks.push(block)-1)+"X\n"};Markdown.Extra.prototype.hashExtraInline=function(block){return"~X"+(this.hashBlocks.push(block)-1)+"X"};Markdown.Extra.prototype.unHashExtraBlocks=function(text){var self=this;function recursiveUnHash(){var hasHash=false;text=text.replace(/(?: )?~X(\d+)X(?:<\/p>)?/g,function(wholeMatch,m1){hasHash=true;var key=parseInt(m1,10);return self.hashBlocks[key]});if(hasHash===true){recursiveUnHash()}}recursiveUnHash();return text};Markdown.Extra.prototype.wrapHeaders=function(text){function wrap(text){return"\n"+text+"\n"}text=text.replace(/^.+[ \t]*\n=+[ \t]*\n+/gm,wrap);text=text.replace(/^.+[ \t]*\n-+[ \t]*\n+/gm,wrap);text=text.replace(/^\#{1,6}[ \t]*.+?[ \t]*\#*\n+/gm,wrap);return text};var attrBlock="\\{[ \\t]*((?:[#.][-_:a-zA-Z0-9]+[ \\t]*)+)\\}";var hdrAttributesA=new RegExp("^(#{1,6}.*#{0,6})[ \\t]+"+attrBlock+"[ \\t]*(?:\\n|0x03)","gm");var hdrAttributesB=new RegExp("^(.*)[ \\t]+"+attrBlock+"[ \\t]*\\n"+"(?=[\\-|=]+\\s*(?:\\n|0x03))","gm");var fcbAttributes=new RegExp("^(```[^`\\n]*)[ \\t]+"+attrBlock+"[ \\t]*\\n"+"(?=([\\s\\S]*?)\\n```[ \\t]*(\\n|0x03))","gm");Markdown.Extra.prototype.hashHeaderAttributeBlocks=function(text){var self=this;function attributeCallback(wholeMatch,pre,attr){return"
~XX"+(self.hashBlocks.push(attr)-1)+"XX
\n"+pre+"\n"}text=text.replace(hdrAttributesA,attributeCallback);text=text.replace(hdrAttributesB,attributeCallback);return text};Markdown.Extra.prototype.hashFcbAttributeBlocks=function(text){var self=this;function attributeCallback(wholeMatch,pre,attr){return"~XX"+(self.hashBlocks.push(attr)-1)+"XX
\n"+pre+"\n"}return text.replace(fcbAttributes,attributeCallback)};Markdown.Extra.prototype.applyAttributeBlocks=function(text){var self=this;var blockRe=new RegExp("~XX(\\d+)XX
[\\s]*"+'(?:<(h[1-6]|pre)(?: +class="(\\S+)")?(>[\\s\\S]*?\\2>))',"gm");text=text.replace(blockRe,function(wholeMatch,k,tag,cls,rest){if(!tag){return""}var key=parseInt(k,10);var attributes=self.hashBlocks[key];var id=attributes.match(/#[^\s#.]+/g)||[];var idStr=id[0]?' id="'+id[0].substr(1,id[0].length-1)+'"':"";var classes=attributes.match(/\.[^\s#.]+/g)||[];for(var i=0;i0){classStr=' class="'+classes.join(" ")+'"'}return"<"+tag+idStr+classStr+rest});return text};Markdown.Extra.prototype.tables=function(text){var self=this;var leadingPipe=new RegExp(["^","[ ]{0,3}","[|]","(.+)\\n","[ ]{0,3}","[|]([ ]*[-:]+[-| :]*)\\n","(","(?:[ ]*[|].*\\n?)*",")","(?:\\n|$)"].join(""),"gm");var noLeadingPipe=new RegExp(["^","[ ]{0,3}","(\\S.*[|].*)\\n","[ ]{0,3}","([-:]+[ ]*[|][-| :]*)\\n","(","(?:.*[|].*\\n?)*",")","(?:\\n|$)"].join(""),"gm");text=text.replace(leadingPipe,doTable);text=text.replace(noLeadingPipe,doTable);function doTable(match,header,separator,body,offset,string){header=header.replace(/^ *[|]/m,"");separator=separator.replace(/^ *[|]/m,"");body=body.replace(/^ *[|]/gm,"");header=header.replace(/[|] *$/m,"");separator=separator.replace(/[|] *$/m,"");body=body.replace(/[|] *$/gm,"");alignspecs=separator.split(/ *[|] */);align=[];for(var i=0;i \n","\n"," \n"].join("");for(i=0;i \n\n";var rows=body.split("\n");for(i=0;i",headerHtml,"\n"].join("")}html+=" \n";for(j=0;j ",colHtml,"\n"].join("")}html+="\n"}html+="\n";return self.hashExtraBlock(html)}return text};Markdown.Extra.prototype.stripFootnoteDefinitions=function(text){var self=this;text=text.replace(/\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|$)/g,function(wholeMatch,m1,m2){m1=slugify(m1);m2+="\n";m2=m2.replace(/^[ ]{0,3}/g,"");self.footnotes[m1]=m2;return"\n"});return text};Markdown.Extra.prototype.doFootnotes=function(text){var self=this;if(self.isConvertingFootnote===true){return text}var footnoteCounter=0;text=text.replace(/\[\^(.+?)\]/g,function(wholeMatch,m1){var id=slugify(m1);var footnote=self.footnotes[id]; +if(footnote===undefined){return wholeMatch}footnoteCounter++;self.usedFootnotes.push(id);var html=''+footnoteCounter+"";return self.hashExtraInline(html)});return text};Markdown.Extra.prototype.printFootnotes=function(text){var self=this;if(self.usedFootnotes.length===0){return text}text+='\n\n \n";return text};Markdown.Extra.prototype.fencedCodeBlocks=function(text){function encodeCode(code){code=code.replace(/&/g,"&");code=code.replace(//g,">");code=code.replace(/~D/g,"$$");code=code.replace(/~T/g,"~");return code}var self=this;text=text.replace(/(?:^|\n)```([^`\n]*)\n([\s\S]*?)\n```[ \t]*(?=\n)/g,function(match,m1,m2){var language=trim(m1),codeblock=m2;var preclass=self.googleCodePrettify?' class="prettyprint"':"";var codeclass="";if(language){if(self.googleCodePrettify||self.highlightJs){codeclass=' class="language-'+language+'"'}else{codeclass=' class="'+language+'"'}}var html=["
\n\n\n';for(var i=0;i
\n'+formattedfootnote+' ↩\n\n'}text+=" "].join("");return self.hashExtraBlock(html)});return text};Markdown.Extra.prototype.educatePants=function(text){var self=this;var result="";var blockOffset=0;text.replace(/(?:)|(<)([a-zA-Z1-6]+)([^\n]*?>)([\s\S]*?)(<\/\2>)/g,function(wholeMatch,m1,m2,m3,m4,m5,offset){var token=text.substring(blockOffset,offset);result+=self.applyPants(token);self.smartyPantsLastChar=result.substring(result.length-1);blockOffset=offset+wholeMatch.length;if(!m1){result+=wholeMatch;return}if(!/code|kbd|pre|script|noscript|iframe|math|ins|del|pre/i.test(m2)){m4=self.educatePants(m4)}else{self.smartyPantsLastChar=m4.substring(m4.length-1)}result+=m1+m2+m3+m4+m5});var lastToken=text.substring(blockOffset);result+=self.applyPants(lastToken);self.smartyPantsLastChar=result.substring(result.length-1);return result};function revertPants(wholeMatch,m1){var blockText=m1;blockText=blockText.replace(/&\#8220;/g,'"');blockText=blockText.replace(/&\#8221;/g,'"');blockText=blockText.replace(/&\#8216;/g,"'");blockText=blockText.replace(/&\#8217;/g,"'");blockText=blockText.replace(/&\#8212;/g,"---");blockText=blockText.replace(/&\#8211;/g,"--");blockText=blockText.replace(/&\#8230;/g,"...");return blockText}Markdown.Extra.prototype.applyPants=function(text){text=text.replace(/---/g,"—").replace(/--/g,"–");text=text.replace(/\.\.\./g,"…").replace(/\.\s\.\s\./g,"…");text=text.replace(/``/g,"“").replace(/''/g,"”");if(/^'$/.test(text)){if(/\S/.test(this.smartyPantsLastChar)){return"’"}return"‘"}if(/^"$/.test(text)){if(/\S/.test(this.smartyPantsLastChar)){return"”"}return"“"}text=text.replace(/^'(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/,"’");text=text.replace(/^"(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/,"”");text=text.replace(/"'(?=\w)/g,"“‘");text=text.replace(/'"(?=\w)/g,"‘“");text=text.replace(/'(?=\d{2}s)/g,"’");text=text.replace(/(\s| |--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)'(?=\w)/g,"$1‘");text=text.replace(/([^\s\[\{\(\-])'/g,"$1’");text=text.replace(/'(?=\s|s\b)/g,"’");text=text.replace(/'/g,"‘");text=text.replace(/(\s| |--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)"(?=\w)/g,"$1“");text=text.replace(/([^\s\[\{\(\-])"/g,"$1”");text=text.replace(/"(?=\s)/g,"”");text=text.replace(/"/ig,"“");return text};Markdown.Extra.prototype.runSmartyPants=function(text){this.smartyPantsLastChar="";text=this.educatePants(text);text=text.replace(/(<([a-zA-Z1-6]+)\b([^\n>]*?)(\/)?>)/g,revertPants);return text};Markdown.Extra.prototype.definitionLists=function(text){var wholeList=new RegExp(["(\\x02\\n?|\\n\\n)","(?:","(","(","[ ]{0,3}","((?:[ \\t]*\\S.*\\n)+)","\\n?","[ ]{0,3}:[ ]+",")","([\\s\\S]+?)","(","(?=\\0x03)","|","(?=","\\n{2,}","(?=\\S)","(?!","[ ]{0,3}","(?:\\S.*\\n)+?","\\n?","[ ]{0,3}:[ ]+",")","(?!","[ ]{0,3}:[ ]+",")",")",")",")",")"].join(""),"gm");var self=this;text=addAnchors(text);text=text.replace(wholeList,function(match,pre,list){var result=trim(self.processDefListItems(list));result="",encodeCode(codeblock),"
\n"+result+"\n
";return pre+self.hashExtraBlock(result)+"\n\n"});return removeAnchors(text)};Markdown.Extra.prototype.processDefListItems=function(listStr){var self=this;var dt=new RegExp(["(\\x02\\n?|\\n\\n+)","(","[ ]{0,3}","(?![:][ ]|[ ])","(?:\\S.*\\n)+?",")","(?=\\n?[ ]{0,3}:[ ])"].join(""),"gm");var dd=new RegExp(["\\n(\\n+)?","(","[ ]{0,3}","[:][ ]+",")","([\\s\\S]+?)","(?=\\n*","(?:","\\n[ ]{0,3}[:][ ]|","|\\x03",")",")"].join(""),"gm"); +listStr=addAnchors(listStr);listStr=listStr.replace(/\n{2,}(?=\\x03)/,"\n");listStr=listStr.replace(dt,function(match,pre,termsStr){var terms=trim(termsStr).split("\n");var text="";for(var i=0;i "}return text+"\n"});listStr=listStr.replace(dd,function(match,leadingLine,markerSpace,def){if(leadingLine||def.match(/\n{2,}/)){def=Array(markerSpace.length+1).join(" ")+def;def=outdent(def)+"\n\n";def="\n"+convertAll(def,self)+"\n"}else{def=rtrim(def);def=convertSpans(outdent(def),self)}return"\n"+term+" "+def+" \n"});return removeAnchors(listStr)};Markdown.Extra.prototype.strikethrough=function(text){return text.replace(/([\W_]|^)~T~T(?=\S)([^\r]*?\S[\*_]*)~T~T([\W_]|$)/g,"$1$2$3")};Markdown.Extra.prototype.newlines=function(text){return text.replace(/(<(?:br|\/li)>)?\n/g,function(wholeMatch,previousTag){return previousTag?wholeMatch:"
\n"})}})(); + + +(function() { + + // Create the converter and the editor + var converter = new Markdown.Converter(); + var options = { + _DoItalicsAndBold: function(text) { + // Restore original markdown implementation + text = text.replace(/(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\1/g, + "$2"); + text = text.replace(/(\*|_)(?=\S)(.+?)(?=\S)\1/g, + "$2"); + return text; + } + }; + converter.setOptions(options); + + function loadJs(src, callback) { + var _doc = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.setAttribute('type', 'text/javascript'); + script.setAttribute('src', src); + _doc.appendChild(script); + script.onload = script.onreadystatechange = function() { + if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){ + callback && callback(); + } + script.onload = script.onreadystatechange = null; + } + } + + function _each(list, callback) { + if(list && list.length > 0) { + for(var i = 0; i < list.length; i++) { + callback(list[i]); + } + } + } + function _has(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // markdown extra + function initMarkdownExtra() { + // Create the converter and the editor + // var converter = new Markdown.Converter(); + var options = { + _DoItalicsAndBold: function(text) { + // Restore original markdown implementation + text = text.replace(/(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\1/g, + "$2"); + text = text.replace(/(\*|_)(?=\S)(.+?)(?=\S)\1/g, + "$2"); + return text; + } + }; + converter.setOptions(options); + + //================ + // markdown exstra + + var markdownExtra = {}; + markdownExtra.config = { + extensions: [ + "fenced_code_gfm", + "tables", + "def_list", + "attr_list", + "footnotes", + "smartypants", + "strikethrough", + "newlines" + ], + intraword: true, + comments: true, + highlighter: "highlight" + }; + var extraOptions = { + extensions: markdownExtra.config.extensions, + highlighter: "prettify" + }; + + if(markdownExtra.config.intraword === true) { + var converterOptions = { + _DoItalicsAndBold: function(text) { + text = text.replace(/([^\w*]|^)(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\2(?=[^\w*]|$)/g, "$1$3"); + text = text.replace(/([^\w*]|^)(\*|_)(?=\S)(.+?)(?=\S)\2(?=[^\w*]|$)/g, "$1$3"); + // Redo bold to handle _**word**_ + text = text.replace(/([^\w*]|^)(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\2(?=[^\w*]|$)/g, "$1$3"); + return text; + } + }; + converter.setOptions(converterOptions); + } + + if(markdownExtra.config.comments === true) { + converter.hooks.chain("postConversion", function(text) { + return text.replace(//g, function(wholeMatch) { + return wholeMatch.replace(/^$/, ' $1 '); + }); + }); + } + + // email & todolist + converter.hooks.chain("postConversion", function(text) { + // email + text = text.replace(/<(mailto\:)?([^\s>]+@[^\s>]+\.\S+?)>/g, function(match, mailto, email) { + return '' + email + ''; + }); + // todolist + text = text.replace(/( )?\[([ xX]?)\] /g, function(matched, p, b) { + p || (p = ''); + return !(b == 'x' || b == 'X') ? '
' + p + ' ' : ' ' + p + ' ' + }); + return text; + }); + + Markdown.Extra.init(converter, extraOptions); + } + + //============== + // toc start + + function initToc() { + var toc = {}; + toc.config = { + marker: "\\[(TOC|toc)\\]", + maxDepth: 6, + button: true, + }; + + // TOC element description + function TocElement(tagName, anchor, text) { + this.tagName = tagName; + this.anchor = anchor; + this.text = text; + this.children = []; + } + TocElement.prototype.childrenToString = function() { + if(this.children.length === 0) { + return ""; + } + var result = " \n"; + _each(this.children, function(child) { + result += child.toString(); + }); + result += "
\n"; + return result; + }; + TocElement.prototype.toString = function() { + var result = ""; + if(this.anchor && this.text) { + result += '' + this.text + ''; + } + result += this.childrenToString() + " \n"; + return result; + }; + + // Transform flat list of TocElement into a tree + function groupTags(array, level) { + level = level || 1; + var tagName = "H" + level; + var result = []; + + var currentElement; + function pushCurrentElement() { + if(currentElement !== undefined) { + if(currentElement.children.length > 0) { + currentElement.children = groupTags(currentElement.children, level + 1); + } + result.push(currentElement); + } + } + + _each(array, function(element) { + if(element.tagName != tagName) { + if(level !== toc.config.maxDepth) { + if(currentElement === undefined) { + currentElement = new TocElement(); + } + currentElement.children.push(element); + } + } + else { + pushCurrentElement(); + currentElement = element; + } + }); + pushCurrentElement(); + return result; + } + + var utils = {}; + var nonWordChars = new RegExp('[^\\p{L}\\p{N}-]', 'g'); + utils.slugify = function(text) { + return text.toLowerCase().replace(/\s/g, '-') // Replace spaces with - + .replace(nonWordChars, '') // Remove all non-word chars + .replace(/\-\-+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, ''); // Trim - from end of text + }; + + // Build the TOC + var previewContentsElt; + function buildToc(previewContentsElt) { + var anchorList = {}; + function createAnchor(element) { + var id = element.id || utils.slugify(element.textContent) || 'title'; + var anchor = id; + var index = 0; + while (_has(anchorList, anchor)) { + anchor = id + "-" + (++index); + } + anchorList[anchor] = true; + // Update the id of the element + element.id = anchor; + return anchor; + } + + var elementList = []; + _each(previewContentsElt.querySelectorAll('h1, h2, h3, h4, h5, h6'), function(elt) { + elementList.push(new TocElement(elt.tagName, createAnchor(elt), elt.textContent)); + }); + elementList = groupTags(elementList); + return '\n\n'; + } + + toc.convert = function(previewContentsElt) { + var tocExp = new RegExp("^\\s*" + toc.config.marker + "\\s*$"); + var tocEltList = document.querySelectorAll('.table-of-contents, .toc'); + var htmlToc = buildToc(previewContentsElt); + // Replace toc paragraphs + _each(previewContentsElt.getElementsByTagName('p'), function(elt) { + if(tocExp.test(elt.innerHTML)) { + elt.innerHTML = htmlToc; + } + }); + // Add toc in the TOC button + _each(tocEltList, function(elt) { + elt.innerHTML = htmlToc; + }); + } + + return toc; + } + + //=========== + // mathjax + // function initMathJax() { + // // 配置 + // MathJax.Hub.Config({ + // skipStartupTypeset: true, + // "HTML-CSS": { + // preferredFont: "TeX", + // availableFonts: [ + // "STIX", + // "TeX" + // ], + // linebreaks: { + // automatic: true + // }, + // EqnChunk: 10, + // imageFont: null + // }, + // tex2jax: { inlineMath: [["$","$"],["\\\\(","\\\\)"]], displayMath: [["$$","$$"],["\\[","\\]"]], processEscapes: true }, + // TeX: { + // noUndefined: { + // attributes: { + // mathcolor: "red", + // mathbackground: "#FFEEEE", + // mathsize: "90%" + // } + // }, + // Safe: { + // allow: { + // URLs: "safe", + // classes: "safe", + // cssIDs: "safe", + // styles: "safe", + // fontsize: "all" + // } + // } + // }, + // messageStyle: "none" + // }); + + // var mathJax = {}; + // mathJax.config = { + // tex : "{}", + // tex2jax: '{ inlineMath: [["$","$"],["\\\\\\\\(","\\\\\\\\)"]], displayMath: [["$$","$$"],["\\\\[","\\\\]"]], processEscapes: true }' + // }; + + // mathJax.init = function(p) { + // converter.hooks.chain("preConversion", removeMath); + // converter.hooks.chain("postConversion", replaceMath); + // }; + + // // From math.stackexchange.com... + + // // + // // The math is in blocks i through j, so + // // collect it into one block and clear the others. + // // Replace &, <, and > by named entities. + // // For IE, put\n' + elementList.join("") + '
\n
at the ends of comments since IE removes \n. + // // Clear the current math positions and store the index of the + // // math, then push the math string onto the storage array. + // // + // function processMath(i, j, unescape) { + // var block = blocks.slice(i, j + 1).join("") + // .replace(/&/g, "&") + // .replace(//g, ">"); + // for(HUB.Browser.isMSIE && (block = block.replace(/(%[^\n]*)\n/g, "$1
\n")); j > i;) + // blocks[j] = "", j--; + // blocks[i] = "@@" + math.length + "@@"; + // unescape && (block = unescape(block)); + // math.push(block); + // start = end = last = null; + // } + + // function removeMath(text) { + // if(!text) { + // return; + // } + // start = end = last = null; + // math = []; + // var unescape; + // if(/`/.test(text)) { + // text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function(text) { + // return text.replace(/\$/g, "~D") + // }); + // unescape = function(text) { + // return text.replace(/~([TD])/g, + // function(match, n) { + // return {T: "~", D: "$"}[n] + // }) + // }; + // } else { + // unescape = function(text) { + // return text + // }; + // } + + // // + // // The pattern for math delimiters and special symbols + // // needed for searching for math in the page. + // // + // var splitDelimiter = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i; + // var split; + + // if(3 === "aba".split(/(b)/).length) { + // split = function(text, delimiter) { + // return text.split(delimiter) + // }; + // } else { + // split = function(text, delimiter) { + // var b = [], c; + // if(!delimiter.global) { + // c = delimiter.toString(); + // var d = ""; + // c = c.replace(/^\/(.*)\/([im]*)$/, function(a, c, b) { + // d = b; + // return c + // }); + // delimiter = RegExp(c, d + "g") + // } + // for(var e = delimiter.lastIndex = 0; c = delimiter.exec(text);) { + // b.push(text.substring(e, c.index)); + // b.push.apply(b, c.slice(1)); + // e = c.index + c[0].length; + // } + // b.push(text.substring(e)); + // return b + // }; + // } + + // blocks = split(text.replace(/\r\n?/g, "\n"), splitDelimiter); + // for(var i = 1, m = blocks.length; i < m; i += 2) { + // var block = blocks[i]; + // if("@" === block.charAt(0)) { + // // + // // Things that look like our math markers will get + // // stored and then retrieved along with the math. + // // + // blocks[i] = "@@" + math.length + "@@"; + // math.push(block) + // } else if(start) { + // // Ignore inline maths that are actually multiline (fixes #136) + // if(end == inline && block.charAt(0) == '\n') { + // if(last) { + // i = last; + // processMath(start, i, unescape); + // } + // start = end = last = null; + // braces = 0; + // } + // // + // // If we are in math, look for the end delimiter, + // // but don't go past double line breaks, and + // // and balance braces within the math. + // // + // else if(block === end) { + // if(braces) { + // last = i + // } else { + // processMath(start, i, unescape) + // } + // } else { + // if(block.match(/\n.*\n/)) { + // if(last) { + // i = last; + // processMath(start, i, unescape); + // } + // start = end = last = null; + // braces = 0; + // } else { + // if("{" === block) { + // braces++ + // } else { + // "}" === block && braces && braces-- + // } + // } + // } + // } else { + // if(block === inline || "$$" === block) { + // start = i; + // end = block; + // braces = 0; + // } else { + // if("begin" === block.substr(1, 5)) { + // start = i; + // end = "\\end" + block.substr(6); + // braces = 0; + // } + // } + // } + + // } + // last && processMath(start, last, unescape); + // return unescape(blocks.join("")); + // } + + // // + // // Put back the math strings that were saved, + // // and clear the math array (no need to keep it around). + // // + // function replaceMath(text) { + // text = text.replace(/@@(\d+)@@/g, function(match, n) { + // return math[n] + // }); + // math = null; + // return text + // } + + // // + // // This is run to restart MathJax after it has finished + // // the previous run (that may have been canceled) + // // + // function startMJ(toElem, callback) { + // var preview = toElem; + // pending = false; + // HUB.cancelTypeset = false; + // HUB.Queue([ + // "Typeset", + // HUB, + // preview + // ]); + // // 执行完后, 再执行 + // HUB.Queue(function() { + // callback && callback(); + // }); + // } + + // var ready = false, pending = false, preview = null, inline = "$", blocks, start, end, last, braces, math, HUB = MathJax.Hub; + + // // + // // Runs after initial typeset + // // + // HUB.Queue(function() { + // ready = true; + // HUB.processUpdateTime = 50; + // HUB.Config({"HTML-CSS": {EqnChunk: 10, EqnChunkFactor: 1}, SVG: {EqnChunk: 10, EqnChunkFactor: 1}}) + // }); + + // mathJax.init(); + // return { + // convert: startMJ + // } + // } + + // function initUml() { + // //=========== + // // uml + // var umlDiagrams = {}; + // umlDiagrams.config = { + // flowchartOptions: [ + // '{', + // ' "line-width": 2,', + // ' "font-family": "sans-serif",', + // ' "font-weight": "normal"', + // '}' + // ].join('\n') + // }; + + // var _loadUmlJs = false; + + // // callback 执行完后执行 + // umlDiagrams.convert = function(target, callback) { + // var previewContentsElt = target; + + // var sequenceElems = previewContentsElt.querySelectorAll('.prettyprint > .language-sequence'); + // var flowElems = previewContentsElt.querySelectorAll('.prettyprint > .language-flow'); + + // function convert() { + // _each(sequenceElems, function(elt) { + // try { + // var diagram = Diagram.parse(elt.textContent); + // var preElt = elt.parentNode; + // var containerElt = crel('div', { + // class: 'sequence-diagram' + // }); + // preElt.parentNode.replaceChild(containerElt, preElt); + // diagram.drawSVG(containerElt, { + // theme: 'simple' + // }); + // } + // catch(e) { + // console.trace(e); + // } + // }); + // _each(flowElems, function(elt) { + // try { + + // var chart = flowchart.parse(elt.textContent); + // var preElt = elt.parentNode; + // var containerElt = crel('div', { + // class: 'flow-chart' + // }); + // preElt.parentNode.replaceChild(containerElt, preElt); + // chart.drawSVG(containerElt, JSON.parse(umlDiagrams.config.flowchartOptions)); + // } + // catch(e) { + // console.error(e); + // } + // }); + + // callback && callback(); + // } + + // if(sequenceElems.length > 0 || flowElems.length > 0) { + // if(!_loadUmlJs) { + // loadJs('./libs/uml.js', function() { + // _loadUmlJs = true; + // convert(); + // }); + // } else { + // convert(); + // } + // } else { + // callback && callback(); + // } + // }; + + // return umlDiagrams; + // } + + // extra是实时的, 同步进行 + initMarkdownExtra(); + + var m; + window.markdownToHtml = function(mdText, toElem, callback) { + var _umlEnd = false; + var _mathJaxEnd = false; + + // 如果是jQuery对象 + if(toElem && !toElem['querySelectorAll'] && toElem['get']) { + toElem = toElem.get(0); + } + if(!toElem || typeof toElem == 'function') { + callback = toElem; + toElem = document.createElement('div'); + } + function _go(mdText, toElem) { + var htmlParsed = converter.makeHtml(mdText); + toElem.innerHTML = htmlParsed; + + // 同步执行 + var toc = initToc(); + toc.convert(toElem); + + // 异步执行 + // var umlDiagrams = initUml(); + // umlDiagrams.convert(toElem, function() { + // _umlEnd = true; + // if(_mathJaxEnd) { + // callback && callback(toElem.innerHTML); + // } + // }); + } + + // 表示有mathjax? + // 加载mathJax + // if(mdText.indexOf('$') !== -1) { + // loadJs("./libs/MathJax/MathJax.js?config=TeX-AMS_HTML", function() { + // if(!m) { + // var m = initMathJax(); + // } + // // 放到后面, 不然removeMathJax()不运行, bug + // _go(mdText, toElem); + // m.convert(toElem, function() { + // _mathJaxEnd = true; + // if(_umlEnd) { + // callback && callback(toElem.innerHTML); + // } + // }); + // }); + // } else { + // _mathJaxEnd = true; + // _go(mdText, toElem); + // } + _go(mdText, toElem); + } + +})(); diff --git a/searx/static/themes/magi/package.json b/searx/static/themes/magi/package.json new file mode 100644 index 000000000..530a96c88 --- /dev/null +++ b/searx/static/themes/magi/package.json @@ -0,0 +1,39 @@ +{ + "devDependencies": { + "eslint": "^8.18.0", + "grunt": "~1.6.1", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-cssmin": "^4.0.0", + "grunt-contrib-less": "~3.0.0", + "grunt-contrib-uglify": "~5.2.1", + "grunt-xmlmin": "~0.1.8", + "grunt-contrib-watch": "~1.1.0", + "grunt-eslint": "^24.0.0", + "grunt-stylelint": "^0.16.0", + "grunt-image": "^6.4.0", + "ionicons": "^6.0.2", + "less": "^4.1.3", + "less-plugin-clean-css": "^1.5.1", + "sharp": "^0.31.0", + "stylelint": "^13.13.1", + "stylelint-config-standard": "^22.0.0", + "ejs": "^3.1.8", + "svgo": "^3.0.0" + }, + "dependencies": { + "autocomplete-js": "2.7.1", + "leaflet": "^1.8.0", + "normalize.css": "^8.0.1" + }, + "scripts": { + "all": "npm install && grunt", + "build": "grunt", + "test": "grunt test", + "eslint": "grunt eslint", + "eslint-fix": "grunt eslint --fix", + "watch": "grunt watch", + "clean": "rm -Rf node_modules package-lock.json", + "stylelint": "grunt stylelint", + "stylelint-fix": "grunt stylelint --fix" + } +} diff --git a/searx/static/themes/magi/src/generated/pygments.less b/searx/static/themes/magi/src/generated/pygments.less new file mode 100644 index 000000000..d1b6efb7a --- /dev/null +++ b/searx/static/themes/magi/src/generated/pygments.less @@ -0,0 +1,99 @@ +/* + this file is generated automatically by searxng_extra/update/update_pygments.py + using pygments version 2.14.0 +*/ + +.code-highlight .linenos { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: default; + + &::selection { + background: transparent; /* WebKit/Blink Browsers */ + } + &::-moz-selection { + background: transparent; /* Gecko Browsers */ + } + + margin-right: 8px; + text-align: right; +} + +.code-highlight pre { line-height: 125%; } +.code-highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.code-highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.code-highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.code-highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.code-highlight .hll { background-color: #ffffcc } +.code-highlight { background: #f8f8f8; } +.code-highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.code-highlight .err { border: 1px solid #FF0000 } /* Error */ +.code-highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.code-highlight .o { color: #666666 } /* Operator */ +.code-highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.code-highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.code-highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.code-highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.code-highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.code-highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.code-highlight .gd { color: #A00000 } /* Generic.Deleted */ +.code-highlight .ge { font-style: italic } /* Generic.Emph */ +.code-highlight .gr { color: #E40000 } /* Generic.Error */ +.code-highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.code-highlight .gi { color: #008400 } /* Generic.Inserted */ +.code-highlight .go { color: #717171 } /* Generic.Output */ +.code-highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.code-highlight .gs { font-weight: bold } /* Generic.Strong */ +.code-highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.code-highlight .gt { color: #0044DD } /* Generic.Traceback */ +.code-highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.code-highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.code-highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.code-highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.code-highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.code-highlight .kt { color: #B00040 } /* Keyword.Type */ +.code-highlight .m { color: #666666 } /* Literal.Number */ +.code-highlight .s { color: #BA2121 } /* Literal.String */ +.code-highlight .na { color: #687822 } /* Name.Attribute */ +.code-highlight .nb { color: #008000 } /* Name.Builtin */ +.code-highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.code-highlight .no { color: #880000 } /* Name.Constant */ +.code-highlight .nd { color: #AA22FF } /* Name.Decorator */ +.code-highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.code-highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.code-highlight .nf { color: #0000FF } /* Name.Function */ +.code-highlight .nl { color: #767600 } /* Name.Label */ +.code-highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.code-highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.code-highlight .nv { color: #19177C } /* Name.Variable */ +.code-highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.code-highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.code-highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.code-highlight .mf { color: #666666 } /* Literal.Number.Float */ +.code-highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.code-highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.code-highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.code-highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.code-highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.code-highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.code-highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.code-highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.code-highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.code-highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.code-highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.code-highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.code-highlight .sx { color: #008000 } /* Literal.String.Other */ +.code-highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.code-highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.code-highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.code-highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.code-highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.code-highlight .vc { color: #19177C } /* Name.Variable.Class */ +.code-highlight .vg { color: #19177C } /* Name.Variable.Global */ +.code-highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.code-highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.code-highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/searx/static/themes/magi/src/js/head/00_init.js b/searx/static/themes/magi/src/js/head/00_init.js new file mode 100644 index 000000000..4aeece8c2 --- /dev/null +++ b/searx/static/themes/magi/src/js/head/00_init.js @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ +(function (w, d) { + 'use strict'; + + // add data- properties + var script = d.currentScript || (function () { + var scripts = d.getElementsByTagName('script'); + return scripts[scripts.length - 1]; + })(); + + w.searxng = { + settings: JSON.parse(atob(script.getAttribute('client_settings'))) + }; + + // update the css + var hmtlElement = d.getElementsByTagName("html")[0]; + hmtlElement.classList.remove('no-js'); + hmtlElement.classList.add('js'); + +})(window, document); diff --git a/searx/static/themes/magi/src/js/main/00_toolkit.js b/searx/static/themes/magi/src/js/main/00_toolkit.js new file mode 100644 index 000000000..e23142412 --- /dev/null +++ b/searx/static/themes/magi/src/js/main/00_toolkit.js @@ -0,0 +1,165 @@ +/** + * @license + * (C) Copyright Contributors to the SearXNG project. + * (C) Copyright Contributors to the searx project (2014 - 2021). + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +window.searxng = (function (w, d) { + + 'use strict'; + + // not invented here tookit with bugs fixed elsewhere + // purposes : be just good enough and as small as possible + + // from https://plainjs.com/javascript/events/live-binding-event-handlers-14/ + if (w.Element) { + (function (ElementPrototype) { + ElementPrototype.matches = ElementPrototype.matches || + ElementPrototype.matchesSelector || + ElementPrototype.webkitMatchesSelector || + ElementPrototype.msMatchesSelector || + function (selector) { + var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1; + while (nodes[++i] && nodes[i] != node); + return !!nodes[i]; + }; + })(Element.prototype); + } + + function callbackSafe (callback, el, e) { + try { + callback.call(el, e); + } catch (exception) { + console.log(exception); + } + } + + var searxng = window.searxng || {}; + + searxng.on = function (obj, eventType, callback, useCapture) { + useCapture = useCapture || false; + if (typeof obj !== 'string') { + // obj HTMLElement, HTMLDocument + obj.addEventListener(eventType, callback, useCapture); + } else { + // obj is a selector + d.addEventListener(eventType, function (e) { + var el = e.target || e.srcElement, found = false; + while (el && el.matches && el !== d && !(found = el.matches(obj))) el = el.parentElement; + if (found) callbackSafe(callback, el, e); + }, useCapture); + } + }; + + searxng.ready = function (callback) { + if (document.readyState != 'loading') { + callback.call(w); + } else { + w.addEventListener('DOMContentLoaded', callback.bind(w)); + } + }; + + searxng.http = function (method, url, data = null) { + return new Promise(function (resolve, reject) { + try { + var req = new XMLHttpRequest(); + req.open(method, url, true); + req.timeout = 20000; + + // On load + req.onload = function () { + if (req.status == 200) { + resolve(req.response, req.responseType); + } else { + reject(Error(req.statusText)); + } + }; + + // Handle network errors + req.onerror = function () { + reject(Error("Network Error")); + }; + + req.onabort = function () { + reject(Error("Transaction is aborted")); + }; + + req.ontimeout = function () { + reject(Error("Timeout")); + } + + // Make the request + if (data) { + req.send(data) + } else { + req.send(); + } + } catch (ex) { + reject(ex); + } + }); + }; + + searxng.loadStyle = function (src) { + var path = searxng.settings.theme_static_path + "/" + src, + id = "style_" + src.replace('.', '_'), + s = d.getElementById(id); + if (s === null) { + s = d.createElement('link'); + s.setAttribute('id', id); + s.setAttribute('rel', 'stylesheet'); + s.setAttribute('type', 'text/css'); + s.setAttribute('href', path); + d.body.appendChild(s); + } + }; + + searxng.loadScript = function (src, callback) { + var path = searxng.settings.theme_static_path + "/" + src, + id = "script_" + src.replace('.', '_'), + s = d.getElementById(id); + if (s === null) { + s = d.createElement('script'); + s.setAttribute('id', id); + s.setAttribute('src', path); + s.onload = callback; + s.onerror = function () { + s.setAttribute('error', '1'); + }; + d.body.appendChild(s); + } else if (!s.hasAttribute('error')) { + try { + callback.apply(s, []); + } catch (exception) { + console.log(exception); + } + } else { + console.log("callback not executed : script '" + path + "' not loaded."); + } + }; + + searxng.insertBefore = function (newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode); + }; + + searxng.insertAfter = function (newNode, referenceNode) { + referenceNode.parentNode.insertAfter(newNode, referenceNode.nextSibling); + }; + + searxng.on('.close', 'click', function () { + this.parentNode.classList.add('invisible'); + }); + + function getEndpoint () { + for (var className of d.getElementsByTagName('body')[0].classList.values()) { + if (className.endsWith('_endpoint')) { + return className.split('_')[0]; + } + } + return ''; + } + + searxng.endpoint = getEndpoint(); + + return searxng; +})(window, document); diff --git a/searx/static/themes/magi/src/js/main/infinite_scroll.js b/searx/static/themes/magi/src/js/main/infinite_scroll.js new file mode 100644 index 000000000..07db3305a --- /dev/null +++ b/searx/static/themes/magi/src/js/main/infinite_scroll.js @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +/* global searxng */ + +searxng.ready(function () { + 'use strict'; + + searxng.infinite_scroll_supported = ( + 'IntersectionObserver' in window && + 'IntersectionObserverEntry' in window && + 'intersectionRatio' in window.IntersectionObserverEntry.prototype); + + if (searxng.endpoint !== 'results') { + return; + } + + if (!searxng.infinite_scroll_supported) { + console.log('IntersectionObserver not supported'); + return; + } + + let d = document; + var onlyImages = d.getElementById('results').classList.contains('only_template_images'); + + function newLoadSpinner () { + var loader = d.createElement('div'); + loader.classList.add('loader'); + return loader; + } + + function replaceChildrenWith (element, children) { + element.textContent = ''; + children.forEach(child => element.appendChild(child)); + } + + function loadNextPage (callback) { + var form = d.querySelector('#pagination form.next_page'); + if (!form) { + return + } + replaceChildrenWith(d.querySelector('#pagination'), [ newLoadSpinner() ]); + var formData = new FormData(form); + searxng.http('POST', d.querySelector('#search').getAttribute('action'), formData).then( + function (response) { + var nextPageDoc = new DOMParser().parseFromString(response, 'text/html'); + var articleList = nextPageDoc.querySelectorAll('#urls article'); + var paginationElement = nextPageDoc.querySelector('#pagination'); + d.querySelector('#pagination').remove(); + if (articleList.length > 0 && !onlyImages) { + // do not add
element when there are only images + d.querySelector('#urls').appendChild(d.createElement('hr')); + } + articleList.forEach(articleElement => { + d.querySelector('#urls').appendChild(articleElement); + }); + if (paginationElement) { + d.querySelector('#results').appendChild(paginationElement); + callback(); + } + } + ).catch( + function (err) { + console.log(err); + var e = d.createElement('div'); + e.textContent = searxng.settings.translations.error_loading_next_page; + e.classList.add('dialog-error'); + e.setAttribute('role', 'alert'); + replaceChildrenWith(d.querySelector('#pagination'), [ e ]); + } + ) + } + + if (searxng.settings.infinite_scroll && searxng.infinite_scroll_supported) { + const intersectionObserveOptions = { + rootMargin: "20rem", + }; + const observedSelector = 'article.result:last-child'; + const observer = new IntersectionObserver(entries => { + const paginationEntry = entries[0]; + if (paginationEntry.isIntersecting) { + observer.unobserve(paginationEntry.target); + loadNextPage(() => observer.observe(d.querySelector(observedSelector), intersectionObserveOptions)); + } + }); + observer.observe(d.querySelector(observedSelector), intersectionObserveOptions); + } + +}); diff --git a/searx/static/themes/magi/src/js/main/keyboard.js b/searx/static/themes/magi/src/js/main/keyboard.js new file mode 100644 index 000000000..0c52673d9 --- /dev/null +++ b/searx/static/themes/magi/src/js/main/keyboard.js @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ +/* global searxng */ + +searxng.ready(function () { + + function isElementInDetail (el) { + while (el !== undefined) { + if (el.classList.contains('detail')) { + return true; + } + if (el.classList.contains('result')) { + // we found a result, no need to go to the root of the document: + // el is not inside aelement + return false; + } + el = el.parentNode; + } + return false; + } + + function getResultElement (el) { + while (el !== undefined) { + if (el.classList.contains('result')) { + return el; + } + el = el.parentNode; + } + return undefined; + } + + function isImageResult (resultElement) { + return resultElement && resultElement.classList.contains('result-images'); + } + + searxng.on('.result', 'click', function (e) { + if (!isElementInDetail(e.target)) { + highlightResult(this)(true, true); + let resultElement = getResultElement(e.target); + if (isImageResult(resultElement)) { + e.preventDefault(); + searxng.selectImage(resultElement); + } + } + }); + + searxng.on('.result a', 'focus', function (e) { + if (!isElementInDetail(e.target)) { + let resultElement = getResultElement(e.target); + if (resultElement && resultElement.getAttribute("data-vim-selected") === null) { + highlightResult(resultElement)(true); + } + if (isImageResult(resultElement)) { + searxng.selectImage(resultElement); + } + } + }, true); + + var vimKeys = { + 27: { + key: 'Escape', + fun: removeFocus, + des: 'remove focus from the focused input', + cat: 'Control' + }, + 73: { + key: 'i', + fun: searchInputFocus, + des: 'focus on the search input', + cat: 'Control' + }, + 66: { + key: 'b', + fun: scrollPage(-window.innerHeight), + des: 'scroll one page up', + cat: 'Navigation' + }, + 70: { + key: 'f', + fun: scrollPage(window.innerHeight), + des: 'scroll one page down', + cat: 'Navigation' + }, + 85: { + key: 'u', + fun: scrollPage(-window.innerHeight / 2), + des: 'scroll half a page up', + cat: 'Navigation' + }, + 68: { + key: 'd', + fun: scrollPage(window.innerHeight / 2), + des: 'scroll half a page down', + cat: 'Navigation' + }, + 71: { + key: 'g', + fun: scrollPageTo(-document.body.scrollHeight, 'top'), + des: 'scroll to the top of the page', + cat: 'Navigation' + }, + 86: { + key: 'v', + fun: scrollPageTo(document.body.scrollHeight, 'bottom'), + des: 'scroll to the bottom of the page', + cat: 'Navigation' + }, + 75: { + key: 'k', + fun: highlightResult('up'), + des: 'select previous search result', + cat: 'Results' + }, + 74: { + key: 'j', + fun: highlightResult('down'), + des: 'select next search result', + cat: 'Results' + }, + 80: { + key: 'p', + fun: GoToPreviousPage(), + des: 'go to previous page', + cat: 'Results' + }, + 78: { + key: 'n', + fun: GoToNextPage(), + des: 'go to next page', + cat: 'Results' + }, + 79: { + key: 'o', + fun: openResult(false), + des: 'open search result', + cat: 'Results' + }, + 84: { + key: 't', + fun: openResult(true), + des: 'open the result in a new tab', + cat: 'Results' + }, + 82: { + key: 'r', + fun: reloadPage, + des: 'reload page from the server', + cat: 'Control' + }, + 72: { + key: 'h', + fun: toggleHelp, + des: 'toggle help window', + cat: 'Other' + } + }; + + if (searxng.settings.hotkeys) { + searxng.on(document, "keydown", function (e) { + // check for modifiers so we don't break browser's hotkeys + if (Object.prototype.hasOwnProperty.call(vimKeys, e.keyCode) && !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) { + var tagName = e.target.tagName.toLowerCase(); + if (e.keyCode === 27) { + vimKeys[e.keyCode].fun(e); + } else { + if (e.target === document.body || tagName === 'a' || tagName === 'button') { + e.preventDefault(); + vimKeys[e.keyCode].fun(); + } + } + } + }); + } + + function highlightResult (which) { + return function (noScroll, keepFocus) { + var current = document.querySelector('.result[data-vim-selected]'), + effectiveWhich = which; + if (current === null) { + // no selection : choose the first one + current = document.querySelector('.result'); + if (current === null) { + // no first one : there are no results + return; + } + // replace up/down actions by selecting first one + if (which === "down" || which === "up") { + effectiveWhich = current; + } + } + + var next, results = document.querySelectorAll('.result'); + + if (typeof effectiveWhich !== 'string') { + next = effectiveWhich; + } else { + switch (effectiveWhich) { + case 'visible': + var top = document.documentElement.scrollTop || document.body.scrollTop; + var bot = top + document.documentElement.clientHeight; + + for (var i = 0; i < results.length; i++) { + next = results[i]; + var etop = next.offsetTop; + var ebot = etop + next.clientHeight; + + if ((ebot <= bot) && (etop > top)) { + break; + } + } + break; + case 'down': + next = current.nextElementSibling; + if (next === null) { + next = results[0]; + } + break; + case 'up': + next = current.previousElementSibling; + if (next === null) { + next = results[results.length - 1]; + } + break; + case 'bottom': + next = results[results.length - 1]; + break; + case 'top': + /* falls through */ + default: + next = results[0]; + } + } + + if (next) { + current.removeAttribute('data-vim-selected'); + next.setAttribute('data-vim-selected', 'true'); + if (!keepFocus) { + var link = next.querySelector('h3 a') || next.querySelector('a'); + if (link !== null) { + link.focus(); + } + } + if (!noScroll) { + scrollPageToSelected(); + } + } + }; + } + + function reloadPage () { + document.location.reload(true); + } + + function removeFocus (e) { + const tagName = e.target.tagName.toLowerCase(); + if (document.activeElement && (tagName === 'input' || tagName === 'select' || tagName === 'textarea')) { + document.activeElement.blur(); + } else { + searxng.closeDetail(); + } + } + + function pageButtonClick (css_selector) { + return function () { + var button = document.querySelector(css_selector); + if (button) { + button.click(); + } + }; + } + + function GoToNextPage () { + return pageButtonClick('nav#pagination .next_page button[type="submit"]'); + } + + function GoToPreviousPage () { + return pageButtonClick('nav#pagination .previous_page button[type="submit"]'); + } + + function scrollPageToSelected () { + var sel = document.querySelector('.result[data-vim-selected]'); + if (sel === null) { + return; + } + var wtop = document.documentElement.scrollTop || document.body.scrollTop, + wheight = document.documentElement.clientHeight, + etop = sel.offsetTop, + ebot = etop + sel.clientHeight, + offset = 120; + // first element ? + if ((sel.previousElementSibling === null) && (ebot < wheight)) { + // set to the top of page if the first element + // is fully included in the viewport + window.scroll(window.scrollX, 0); + return; + } + if (wtop > (etop - offset)) { + window.scroll(window.scrollX, etop - offset); + } else { + var wbot = wtop + wheight; + if (wbot < (ebot + offset)) { + window.scroll(window.scrollX, ebot - wheight + offset); + } + } + } + + function scrollPage (amount) { + return function () { + window.scrollBy(0, amount); + highlightResult('visible')(); + }; + } + + function scrollPageTo (position, nav) { + return function () { + window.scrollTo(0, position); + highlightResult(nav)(); + }; + } + + function searchInputFocus () { + window.scrollTo(0, 0); + var q = document.querySelector('#q'); + q.focus(); + if (q.setSelectionRange) { + var len = q.value.length; + q.setSelectionRange(len, len); + } + } + + function openResult (newTab) { + return function () { + var link = document.querySelector('.result[data-vim-selected] h3 a'); + if (link === null) { + link = document.querySelector('.result[data-vim-selected] > a'); + } + if (link !== null) { + var url = link.getAttribute('href'); + if (newTab) { + window.open(url); + } else { + window.location.href = url; + } + } + }; + } + + function initHelpContent (divElement) { + var categories = {}; + + for (var k in vimKeys) { + var key = vimKeys[k]; + categories[key.cat] = categories[key.cat] || []; + categories[key.cat].push(key); + } + + var sorted = Object.keys(categories).sort(function (a, b) { + return categories[b].length - categories[a].length; + }); + + if (sorted.length === 0) { + return; + } + + var html = '×'; + html += 'How to navigate searx with Vim-like hotkeys
'; + html += ''; + + for (var i = 0; i < sorted.length; i++) { + var cat = categories[sorted[i]]; + + var lastCategory = i === (sorted.length - 1); + var first = i % 2 === 0; + + if (first) { + html += '
'; + + divElement.innerHTML = html; + } + + function toggleHelp () { + var helpPanel = document.querySelector('#vim-hotkeys-help'); + if (helpPanel === undefined || helpPanel === null) { + // first call + helpPanel = document.createElement('div'); + helpPanel.id = 'vim-hotkeys-help'; + helpPanel.className = 'dialog-modal'; + initHelpContent(helpPanel); + initHelpContent(helpPanel); + initHelpContent(helpPanel); + var body = document.getElementsByTagName('body')[0]; + body.appendChild(helpPanel); + } else { + // togggle hidden + helpPanel.classList.toggle('invisible'); + return; + } + } + + searxng.scrollPageToSelected = scrollPageToSelected; + searxng.selectNext = highlightResult('down'); + searxng.selectPrevious = highlightResult('up'); +}); diff --git a/searx/static/themes/magi/src/js/main/mapresult.js b/searx/static/themes/magi/src/js/main/mapresult.js new file mode 100644 index 000000000..419bfa9fa --- /dev/null +++ b/searx/static/themes/magi/src/js/main/mapresult.js @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ +/* global L */ +(function (w, d, searxng) { + 'use strict'; + + searxng.ready(function () { + searxng.on('.searxng_init_map', 'click', function (event) { + // no more request + this.classList.remove("searxng_init_map"); + + // + var leaflet_target = this.dataset.leafletTarget; + var map_lon = parseFloat(this.dataset.mapLon); + var map_lat = parseFloat(this.dataset.mapLat); + var map_zoom = parseFloat(this.dataset.mapZoom); + var map_boundingbox = JSON.parse(this.dataset.mapBoundingbox); + var map_geojson = JSON.parse(this.dataset.mapGeojson); + + searxng.loadStyle('css/leaflet.css'); + searxng.loadScript('js/leaflet.js', function () { + var map_bounds = null; + if (map_boundingbox) { + var southWest = L.latLng(map_boundingbox[0], map_boundingbox[2]); + var northEast = L.latLng(map_boundingbox[1], map_boundingbox[3]); + map_bounds = L.latLngBounds(southWest, northEast); + } + + // init map + var map = L.map(leaflet_target); + // create the tile layer with correct attribution + var osmMapnikUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; + var osmMapnikAttrib = 'Map data © OpenStreetMap contributors'; + var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib}); + var osmWikimediaUrl = 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png'; + var osmWikimediaAttrib = 'Wikimedia maps | Maps data © OpenStreetMap contributors'; + var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib}); + // init map view + if (map_bounds) { + // TODO hack: https://github.com/Leaflet/Leaflet/issues/2021 + // Still useful ? + setTimeout(function () { + map.fitBounds(map_bounds, { + maxZoom: 17 + }); + }, 0); + } else if (map_lon && map_lat) { + if (map_zoom) { + map.setView(new L.latLng(map_lat, map_lon), map_zoom); + } else { + map.setView(new L.latLng(map_lat, map_lon), 8); + } + } + + map.addLayer(osmMapnik); + + var baseLayers = { + "OSM Mapnik": osmMapnik, + "OSM Wikimedia": osmWikimedia, + }; + + L.control.layers(baseLayers).addTo(map); + + if (map_geojson) { + L.geoJson(map_geojson).addTo(map); + } /* else if(map_bounds) { + L.rectangle(map_bounds, {color: "#ff7800", weight: 3, fill:false}).addTo(map); + } */ + }); + + // this event occour only once per element + event.preventDefault(); + }); + }); +})(window, document, window.searxng); diff --git a/searx/static/themes/magi/src/js/main/preferences.js b/searx/static/themes/magi/src/js/main/preferences.js new file mode 100644 index 000000000..1e3ae5981 --- /dev/null +++ b/searx/static/themes/magi/src/js/main/preferences.js @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ +(function (w, d, searxng) { + 'use strict'; + + if (searxng.endpoint !== 'preferences') { + return; + } + + searxng.ready(function () { + let engine_descriptions = null; + function load_engine_descriptions () { + if (engine_descriptions == null) { + searxng.http("GET", "engine_descriptions.json").then(function (content) { + engine_descriptions = JSON.parse(content); + for (const [engine_name, description] of Object.entries(engine_descriptions)) { + let elements = d.querySelectorAll('[data-engine-name="' + engine_name + '"] .engine-description'); + for (const element of elements) { + let source = ' (' + searxng.settings.translations.Source + ': ' + description[1] + ')'; + element.innerHTML = description[0] + source; + } + } + }); + } + } + + for (const el of d.querySelectorAll('[data-engine-name]')) { + searxng.on(el, 'mouseenter', load_engine_descriptions); + } + }); +})(window, document, window.searxng); diff --git a/searx/static/themes/magi/src/js/main/results.js b/searx/static/themes/magi/src/js/main/results.js new file mode 100644 index 000000000..4eae58435 --- /dev/null +++ b/searx/static/themes/magi/src/js/main/results.js @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ +(function (w, d, searxng) { + 'use strict'; + + if (searxng.endpoint !== 'results') { + return; + } + + searxng.ready(function () { + d.querySelectorAll('#urls img.image').forEach( + img => + img.addEventListener( + 'error', () => { + img.style.display = 'none'; + img.error = null; + } + )); + + searxng.on('.btn-collapse', 'click', function () { + var btnLabelCollapsed = this.getAttribute('data-btn-text-collapsed'); + var btnLabelNotCollapsed = this.getAttribute('data-btn-text-not-collapsed'); + var target = this.getAttribute('data-target'); + var targetElement = d.querySelector(target); + var html = this.innerHTML; + if (this.classList.contains('collapsed')) { + html = html.replace(btnLabelCollapsed, btnLabelNotCollapsed); + } else { + html = html.replace(btnLabelNotCollapsed, btnLabelCollapsed); + } + this.innerHTML = html; + this.classList.toggle('collapsed'); + targetElement.classList.toggle('invisible'); + }); + + searxng.on('.media-loader', 'click', function () { + var target = this.getAttribute('data-target'); + var iframe_load = d.querySelector(target + ' > iframe'); + var srctest = iframe_load.getAttribute('src'); + if (srctest === null || srctest === undefined || srctest === false) { + iframe_load.setAttribute('src', iframe_load.getAttribute('data-src')); + } + }); + + searxng.selectImage = function (resultElement) { + /* eslint no-unused-vars: 0 */ + if (resultElement) { + // load full size image in background + const imgElement = resultElement.querySelector('.result-images-source img'); + const thumbnailElement = resultElement.querySelector('.image_thumbnail'); + const detailElement = resultElement.querySelector('.detail'); + if (imgElement) { + const imgSrc = imgElement.getAttribute('data-src'); + if (imgSrc) { + const loader = d.createElement('div'); + const imgLoader = new Image(); + + loader.classList.add('loader'); + detailElement.appendChild(loader); + + imgLoader.onload = e => { + imgElement.src = imgSrc; + loader.remove(); + }; + imgLoader.onerror = e => { + loader.remove(); + }; + imgLoader.src = imgSrc; + imgElement.src = thumbnailElement.src; + imgElement.removeAttribute('data-src'); + } + } + } + d.getElementById('results').classList.add('image-detail-open'); + searxng.scrollPageToSelected(); + } + + searxng.closeDetail = function (e) { + d.getElementById('results').classList.remove('image-detail-open'); + searxng.scrollPageToSelected(); + } + searxng.on('.result-detail-close', 'click', e => { + e.preventDefault(); + searxng.closeDetail(); + }); + searxng.on('.result-detail-previous', 'click', e => searxng.selectPrevious(false)); + searxng.on('.result-detail-next', 'click', e => searxng.selectNext(false)); + + w.addEventListener('scroll', function () { + var e = d.getElementById('backToTop'), + scrollTop = document.documentElement.scrollTop || document.body.scrollTop, + results = d.getElementById('results'); + if (e !== null) { + if (scrollTop >= 100) { + results.classList.add('scrolling'); + } else { + results.classList.remove('scrolling'); + } + } + }, true); + + }); + +})(window, document, window.searxng); diff --git a/searx/static/themes/magi/src/js/main/search.js b/searx/static/themes/magi/src/js/main/search.js new file mode 100644 index 000000000..9ba3914be --- /dev/null +++ b/searx/static/themes/magi/src/js/main/search.js @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ +/* global AutoComplete */ +(function (w, d, searxng) { + 'use strict'; + + var qinput_id = "q", qinput; + + const isMobile = window.matchMedia("only screen and (max-width: 50em)").matches; + + function submitIfQuery () { + if (qinput.value.length > 0) { + var search = document.getElementById('search'); + setTimeout(search.submit.bind(search), 0); + } + } + + function createClearButton (qinput) { + var cs = document.getElementById('clear_search'); + var updateClearButton = function () { + if (qinput.value.length === 0) { + cs.classList.add("empty"); + } else { + cs.classList.remove("empty"); + } + }; + + // update status, event listener + updateClearButton(); + cs.addEventListener('click', function (ev) { + qinput.value = ''; + qinput.focus(); + updateClearButton(); + ev.preventDefault(); + }); + qinput.addEventListener('keyup', updateClearButton, false); + } + + searxng.ready(function () { + qinput = d.getElementById(qinput_id); + + if (qinput !== null) { + // clear button + createClearButton(qinput); + + // autocompleter + if (searxng.settings.autocomplete_provider) { + searxng.autocomplete = AutoComplete.call(w, { + Url: "./autocompleter", + EmptyMessage: searxng.settings.translations.no_item_found, + HttpMethod: searxng.settings.http_method, + HttpHeaders: { + "Content-type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest" + }, + MinChars: searxng.settings.autocomplete_min, + Delay: 300, + _Position: function () {}, + _Open: function () { + var params = this; + Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"), function (li) { + if (li.getAttribute("class") != "locked") { + li.onmousedown = function () { + params._Select(li); + }; + } + }); + }, + }, "#" + qinput_id); + } + + /* + Monkey patch autocomplete.js to fix a bug + With the POST method, the values are not URL encoded: query like "1 + 1" are sent as "1 1" since space are URL encoded as plus. + See HTML specifications: + * HTML5: https://url.spec.whatwg.org/#concept-urlencoded-serializer + * HTML4: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 + + autocomplete.js does not URL encode the name and values: + https://github.com/autocompletejs/autocomplete.js/blob/87069524f3b95e68f1b54d8976868e0eac1b2c83/src/autocomplete.ts#L665 + + The monkey patch overrides the compiled version of the ajax function. + See https://github.com/autocompletejs/autocomplete.js/blob/87069524f3b95e68f1b54d8976868e0eac1b2c83/dist/autocomplete.js#L143-L158 + The patch changes only the line 156 from + params.Request.send(params._QueryArg() + "=" + params._Pre()); + to + params.Request.send(encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(params._Pre())); + + Related to: + * https://github.com/autocompletejs/autocomplete.js/issues/78 + * https://github.com/searxng/searxng/issues/1695 + */ + AutoComplete.prototype.ajax = function (params, request, timeout) { + if (timeout === void 0) { timeout = true; } + if (params.$AjaxTimer) { + window.clearTimeout(params.$AjaxTimer); + } + if (timeout === true) { + params.$AjaxTimer = window.setTimeout(AutoComplete.prototype.ajax.bind(null, params, request, false), params.Delay); + } else { + if (params.Request) { + params.Request.abort(); + } + params.Request = request; + params.Request.send(encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(params._Pre())); + } + }; + + if (!isMobile && document.querySelector('.index_endpoint')) { + qinput.focus(); + } + } + + // vanilla js version of search_on_category_select.js + if (qinput !== null && d.querySelector('.help') != null && searxng.settings.search_on_category_select) { + d.querySelector('.help').className = 'invisible'; + + searxng.on('#categories input', 'change', function () { + var i, categories = d.querySelectorAll('#categories input[type="checkbox"]'); + for (i = 0; i < categories.length; i++) { + if (categories[i] !== this && categories[i].checked) { + categories[i].click(); + } + } + if (! this.checked) { + this.click(); + } + submitIfQuery(); + return false; + }); + + searxng.on(d.getElementById('safesearch'), 'change', submitIfQuery); + searxng.on(d.getElementById('time_range'), 'change', submitIfQuery); + searxng.on(d.getElementById('language'), 'change', submitIfQuery); + } + + }); + +})(window, document, window.searxng); diff --git a/searx/static/themes/magi/src/less/animations.less b/searx/static/themes/magi/src/less/animations.less new file mode 100644 index 000000000..75c98308f --- /dev/null +++ b/searx/static/themes/magi/src/less/animations.less @@ -0,0 +1,19 @@ +.dialog-modal { + animation-name: dialogmodal; + animation-duration: 0.13s; + + @keyframes dialogmodal { + 0% { + opacity: 0; + } + + 50% { + opacity: 0.5; + transform: translate(-50%, -50%) scale(1.05); + } + } +} + +input.checkbox-onoff[type="checkbox"]::before { + transition: left 0.25s; +} diff --git a/searx/static/themes/magi/src/less/autocomplete.less b/searx/static/themes/magi/src/less/autocomplete.less new file mode 100644 index 000000000..bb2e1890f --- /dev/null +++ b/searx/static/themes/magi/src/less/autocomplete.less @@ -0,0 +1,75 @@ +/*! Autocomplete.js v2.6.3 | license MIT | (c) 2017, Baptiste Donaux | http://autocomplete-js.com */ + +.autocomplete { + position: absolute; + width: @search-width; + max-height: 0; + overflow-y: hidden; + .ltr-text-align-left(); + + .rounded-corners; + + &:active, + &:focus, + &:hover { + background-color: var(--color-autocomplete-background); + } + + &:empty { + display: none; + } + + > ul { + list-style-type: none; + margin: 0; + padding: 0; + + > li { + cursor: pointer; + padding: 0.5rem 1rem; + + &.active, + &:active, + &:focus, + &:hover { + background-color: var(--color-autocomplete-background-hover); + + a:active, + a:focus, + a:hover { + text-decoration: none; + } + } + + &.locked { + cursor: inherit; + } + } + } + + &.open { + display: block; + background-color: var(--color-autocomplete-background); + color: var(--color-autocomplete-font); + max-height: 32rem; + overflow-y: auto; + z-index: 100; + margin-top: 3.5rem; + border-radius: 0.8rem; + box-shadow: 0 2px 8px rgb(34 38 46 / 25%); + + &:empty { + display: none; + } + } +} + +@media screen and (max-width: @phone) { + .autocomplete { + width: 100%; + + > ul > li { + padding: 1rem; + } + } +} diff --git a/searx/static/themes/magi/src/less/code.less b/searx/static/themes/magi/src/less/code.less new file mode 100644 index 000000000..f4e6735ba --- /dev/null +++ b/searx/static/themes/magi/src/less/code.less @@ -0,0 +1,9 @@ +.code-highlight pre { + overflow: auto; + background-color: inherit; + color: inherit; + border: inherit; +} + +// stylelint-disable no-invalid-position-at-import-rule +@import "../generated/pygments.less"; diff --git a/searx/static/themes/magi/src/less/definitions.less b/searx/static/themes/magi/src/less/definitions.less new file mode 100644 index 000000000..885826e76 --- /dev/null +++ b/searx/static/themes/magi/src/less/definitions.less @@ -0,0 +1,279 @@ +/* + * SearXNG, A privacy-respecting, hackable metasearch engine + * + * To change the colors of the site, simple edit this variables + */ + +/// Light Theme +:root { + /// Base Colors + --color-base-font: #444; + --color-base-font-rgb: 68, 68, 68; + --color-base-background: #fff; + --color-base-background-mobile: #f2f5f8; + --color-url-font: #334999; + --color-url-visited-font: #9822c3; + /// Header Colors + --color-header-background: #fdfbff; + --color-header-border: #ddd; + /// Footer Colors + --color-footer-background: #fdfbff; + --color-footer-border: #ddd; + /// Sidebar Colors + --color-sidebar-border: #ddd; + --color-sidebar-font: #000; + --color-sidebar-background: #fff; + /// BackToTop Colors + --color-backtotop-font: #444; + --color-backtotop-border: #ddd; + --color-backtotop-background: #fff; + /// Button Colors + --color-btn-background: #3050ff; + --color-btn-font: #fff; + --color-show-btn-background: #bbb; + --color-show-btn-font: #000; + /// Search Input Colors + --color-search-border: #bbb; + --color-search-shadow: 0 2px 8px rgb(34 38 46 / 25%); + --color-search-background: #fff; + --color-search-font: #222; + --color-search-background-hover: #3050ff; + /// Modal Colors + --color-error: #db3434; + --color-error-background: lighten(#db3434, 40%); + --color-warning: #dbba34; + --color-warning-background: lighten(#dbba34, 40%); + --color-success: #42db34; + --color-success-background: lighten(#42db34, 40%); + /// Categories Colors + --color-categories-item-selected-font: #3050ff; + --color-categories-item-border-selected: #3050ff; + /// Autocomplete Colors + --color-autocomplete-font: #000; + --color-autocomplete-border: #bbb; + --color-autocomplete-shadow: 0 2px 8px rgb(34 38 46 / 25%); + --color-autocomplete-background: #fff; + --color-autocomplete-background-hover: #e3e3e3; + /// Answer Colors + --color-answer-font: #444; // same as --color-base-font + --color-answer-background: #fff; + /// Results Colors + --color-result-background: #fff; + --color-result-border: #ddd; + --color-result-url-font: #000; + --color-result-vim-selected: #f7f7f7; + --color-result-vim-arrow: #000bbb; + --color-result-description-highlight-font: #000; + --color-result-link-font: #000bbb; + --color-result-link-font-highlight: #000bbb; + --color-result-link-visited-font: #9822c3; + --color-result-publishdate-font: #777; + --color-result-engines-font: #545454; + --color-result-search-url-border: #ddd; + --color-result-search-url-font: #000; + // Images Colors + --color-result-image-span-font: #444; + --color-result-image-span-font-selected: #fff; + --color-result-image-background: #fff; + /// Settings Colors + --color-settings-tr-hover: #ebebeb; + --color-settings-engine-description-font: #545454; + --color-settings-engine-group-background: #0001; + /// Detail modal + --color-result-detail-font: #fff; + --color-result-detail-label-font: lightgray; + --color-result-detail-background: #242424; + --color-result-detail-hr: #555; + --color-result-detail-link: #8af; + --color-result-detail-loader-border: rgba(255, 255, 255, 0.2); + --color-result-detail-loader-borderleft: rgba(0, 0, 0, 0); + /// Toolkit Colors + --color-toolkit-badge-font: #fff; + --color-toolkit-badge-background: #545454; + --color-toolkit-kbd-font: #fff; + --color-toolkit-kbd-background: #000; + --color-toolkit-dialog-border: #ddd; + --color-toolkit-dialog-background: #fff; + --color-toolkit-tabs-label-border: #fff; + --color-toolkit-tabs-section-border: #ddd; + --color-toolkit-select-background: #e1e1e1; + --color-toolkit-select-border: #ddd; + --color-toolkit-select-background-hover: #bbb; + --color-toolkit-input-text-font: #222; + --color-toolkit-checkbox-onoff-off-background: #ddd; + --color-toolkit-checkbox-onoff-on-background: #ddd; + --color-toolkit-checkbox-onoff-on-mark-background: #3050ff; + --color-toolkit-checkbox-onoff-on-mark-color: #fff; + --color-toolkit-checkbox-onoff-off-mark-background: #aaa; + --color-toolkit-checkbox-onoff-off-mark-color: #fff; + --color-toolkit-checkbox-label-background: #ddd; + --color-toolkit-checkbox-label-border: #ddd; + --color-toolkit-checkbox-input-border: #3050ff; + --color-toolkit-engine-tooltip-border: #ddd; + --color-toolkit-engine-tooltip-background: #fff; + --color-toolkit-loader-border: rgba(0, 0, 0, 0.2); + --color-toolkit-loader-borderleft: rgba(255, 255, 255, 0); + --color-doc-code: #300; + --color-doc-code-background: #fdd; + --invert-logo: invert(0%); +} + +.dark-themes() { + /// Base Colors + --color-base-font: #bbb; + --color-base-font-rgb: 187, 187, 187; + --color-base-background: #222428; + --color-base-background-mobile: #222428; + --color-url-font: #8af; + --color-url-visited-font: #c09cd9; + /// Header Colors + --color-header-background: #1e1e22; + --color-header-border: #333; + /// Footer Colors + --color-footer-background: #1e1e22; + --color-footer-border: #333; + /// Sidebar Colors + --color-sidebar-border: #555; + --color-sidebar-font: #fff; + --color-sidebar-background: #292c34; + /// BackToTop Colors + --color-backtotop-font: #bbb; + --color-backtotop-border: #333; + --color-backtotop-background: #2b2e36; + /// Button Colors + --color-btn-background: linear-gradient(81.62deg, #2870ea 8.72%, #1b4aef 85.01%); + --color-btn-font: #fff; + --color-show-btn-background: #555; + --color-show-btn-font: #fff; + /// Search Input Colors + --color-search-border: #555; + --color-search-shadow: 0 2px 8px rgb(34 38 46 / 25%); + --color-search-background: #2b2e36; + --color-search-font: #fff; + --color-search-background-hover: linear-gradient(81.62deg, #2870ea 8.72%, #1b4aef 85.01%); + /// Modal Colors + --color-error: #f55b5b; + --color-error-background: darken(#db3434, 40%); + --color-warning: #f1d561; + --color-warning-background: darken(#dbba34, 40%); + --color-success: #79f56e; + --color-success-background: darken(#42db34, 40%); + /// Categories Colors + --color-categories-item-selected-font: #58f; + --color-categories-item-border-selected: #58f; + /// Autocomplete Colors + --color-autocomplete-font: #fff; + --color-autocomplete-border: #555; + --color-autocomplete-shadow: 0 2px 8px rgb(34 38 46 / 25%); + --color-autocomplete-background: #2b2e36; + --color-autocomplete-background-hover: #1e1e22; + /// Answer Colors + --color-answer-font: #bbb; // same as --color-base-font + --color-answer-background: #26292f; + /// Results Colors + --color-result-background: #26292f; + --color-result-border: #333; + --color-result-url-font: #fff; + --color-result-vim-selected: #1f1f23cc; + --color-result-vim-arrow: #8af; + --color-result-description-highlight-font: #fff; + --color-result-link-font: #8af; + --color-result-link-font-highlight: #8af; + --color-result-link-visited-font: #c09cd9; + --color-result-publishdate-font: #888; + --color-result-engines-font: #a4a4a4; + --color-result-search-url-border: #555; + --color-result-search-url-font: #fff; + /// Detail modal : same as the light version + --color-result-detail-font: #fff; + --color-result-detail-label-font: lightgray; + --color-result-detail-background: #1a1a1c; + --color-result-detail-hr: #555; + --color-result-detail-link: #8af; + --color-result-detail-loader-border: rgba(255, 255, 255, 0.2); + --color-result-detail-loader-borderleft: rgba(0, 0, 0, 0); + // Images Colors + --color-result-image-span-font: #bbb; + --color-result-image-span-font-selected: #222; + --color-result-image-background: #222; + /// Settings Colors + --color-settings-tr-hover: #2c2c32; + --color-settings-engine-description-font: darken(#dcdcdc, 30%); + --color-settings-engine-group-background: #1b1b21; + /// Toolkit Colors + --color-toolkit-badge-font: #fff; + --color-toolkit-badge-background: #555; + --color-toolkit-kbd-font: #000; + --color-toolkit-kbd-background: #fff; + --color-toolkit-dialog-border: #555; + --color-toolkit-dialog-background: #1e1e22; + --color-toolkit-tabs-label-border: #222; + --color-toolkit-tabs-section-border: #555; + --color-toolkit-select-background: #313338; + --color-toolkit-select-border: #555; + --color-toolkit-select-background-hover: #373b49; + --color-toolkit-input-text-font: #fff; + --color-toolkit-checkbox-onoff-off-background: #313338; + --color-toolkit-checkbox-onoff-on-background: #313338; + --color-toolkit-checkbox-onoff-on-mark-background: #58f; + --color-toolkit-checkbox-onoff-on-mark-color: #222; + --color-toolkit-checkbox-onoff-off-mark-background: #ddd; + --color-toolkit-checkbox-onoff-off-mark-color: #222; + --color-toolkit-checkbox-label-background: #222; + --color-toolkit-checkbox-label-border: #333; + --color-toolkit-checkbox-input-border: #58f; + --color-toolkit-engine-tooltip-border: #333; + --color-toolkit-engine-tooltip-background: #222; + --color-toolkit-loader-border: rgba(255, 255, 255, 0.2); + --color-toolkit-loader-borderleft: rgba(0, 0, 0, 0); + --color-doc-code: #fdd; + --color-doc-code-background: #300; + --invert-logo: invert(100%); +} + +/// Dark Theme (autoswitch based on device pref) +@media (prefers-color-scheme: dark) { + :root.theme-auto { + .dark-themes(); + } +} + +// Dark Theme by preferences +:root.theme-dark { + .dark-themes(); +} + +/// General Size +@results-width: 45rem; +@results-sidebar-width: 25rem; +@results-offset: 10rem; +@results-tablet-offset: 0.5rem; +@results-gap: 5rem; +@results-margin: 0.125rem; +@result-padding: 1rem; +@results-image-row-height: 12rem; +@results-image-row-height-phone: 6rem; +@search-width: 44rem; +// heigh of #search, see detail.less +@search-height: 7.6rem; + +/// Device Size +/// @desktop > @tablet +@tablet: 79.75em; // see https://github.com/searxng/searxng/issues/874 +@phone: 50em; +@small-phone: 35em; +@ultra-small-phone: 20rem; + +/// From style.less +@stacked-bar-chart: rgb(0, 0, 0); + +/// Load fonts from this directory. +@icon-font-path: "../../../fonts/"; +//** File name for all font files. +@icon-font-name: "glyphicons-halflings-regular"; +//** Element ID within SVG icon file. +@icon-font-svg-id: "glyphicons_halflingsregular"; + +// decoration of the select HTML elements +@select-light-svg-path: "../svg/select-light.svg"; +@select-dark-svg-path: "../svg/select-dark.svg"; diff --git a/searx/static/themes/magi/src/less/detail.less b/searx/static/themes/magi/src/less/detail.less new file mode 100644 index 000000000..b63e65206 --- /dev/null +++ b/searx/static/themes/magi/src/less/detail.less @@ -0,0 +1,248 @@ +#main_results #results.image-detail-open.only_template_images { + width: min(98%, 59.25rem) !important; +} + +#main_results #results.only_template_images.image-detail-open #backToTop { + .ltr-left(56.75rem) !important; + .ltr-right(inherit); +} + +article.result-images .detail { + display: none; +} + +#results.image-detail-open article.result-images[data-vim-selected] .detail { + display: flex; + flex-direction: column; + position: fixed; + .ltr-left(60rem); + .ltr-right(0); + top: @search-height; + transition: top 0.064s ease-in 0s; + bottom: 0; + background: var(--color-result-detail-background); + border: 1px solid var(--color-result-detail-background); + z-index: 10000; + padding: 4rem 3rem 3rem 3rem; + + a.result-images-source { + display: block; + flex: 1; + text-align: left; + width: 100%; + border: none; + text-decoration: none; + + img { + padding: 0; + margin: 0; + border: none; + object-fit: contain; + width: inherit; + height: inherit; + max-width: 100%; + min-height: inherit; + max-height: calc(100vh - 25rem - 7rem); + background: inherit; + } + } + + .result-images-labels { + color: var(--color-result-detail-font); + max-height: 16rem; + min-height: 16rem; + + hr { + border-top: 1px solid var(--color-result-detail-hr); + border-bottom: none; + } + + h4 { + height: 2rem; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.9rem; + } + + p { + color: var(--color-result-detail-label-font); + font-size: 0.9rem; + + span { + display: inline-block; + width: 12rem; + } + } + + h4, + p, + a { + .ltr-text-align-left(); + } + + p.result-content { + height: 2rem; + overflow: hidden; + text-overflow: ellipsis; + } + + p.result-url { + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; + } + + p.result-content:hover, + p.result-url:hover { + position: relative; + overflow: inherit !important; + background: var(--color-result-detail-background); + text-overflow: inherit !important; + } + + a, + a:visited, + a:hover, + a:active { + color: var(--color-result-detail-link); + } + + a:hover { + text-decoration: underline; + } + } + + a.result-detail-close { + top: 1rem; + .ltr-left(1rem); + padding: 0.4rem; + } + + a.result-detail-previous { + top: 1rem; + .ltr-right(6rem); + // center the icon by moving it slightly on the left + padding-top: 0.4rem; + .ltr-padding-right(0.5rem); + padding-bottom: 0.4rem; + .ltr-padding-left(0.3rem); + } + + a.result-detail-next { + top: 1rem; + .ltr-right(2rem); + padding: 0.4rem; + } + + a.result-detail-close, + a.result-detail-next, + a.result-detail-previous { + border-radius: 50%; + display: block; + width: 1.5rem; + height: 1.5rem; + position: absolute; + filter: opacity(40%); + z-index: 2000002; + + span { + display: block; + width: 1.5rem; + height: 1.5rem; + text-align: center; + } + } + + a.result-detail-next, + a.result-detail-previous { + span::before { + // vertical center small icons + vertical-align: sub; + } + } + + a.result-detail-close, + a.result-detail-close:visited, + a.result-detail-close:hover, + a.result-detail-close:active, + a.result-detail-previous, + a.result-detail-previous:visited, + a.result-detail-previous:hover, + a.result-detail-previous:active, + a.result-detail-next, + a.result-detail-next:visited, + a.result-detail-next:hover, + a.result-detail-next:active { + color: var(--color-result-detail-font); + background: var(--color-result-detail-background); + border: 1px solid var(--color-result-detail-font); + } + + a.result-detail-close:focus, + a.result-detail-close:hover, + a.result-detail-previous:focus, + a.result-detail-previous:hover, + a.result-detail-next:focus, + a.result-detail-next:hover { + filter: opacity(80%); + } + + .loader { + position: absolute; + top: 1rem; + .ltr-right(50%); + border-top: 0.5em solid var(--color-result-detail-loader-border); + border-right: 0.5em solid var(--color-result-detail-loader-border); + border-bottom: 0.5em solid var(--color-result-detail-loader-border); + border-left: 0.5em solid var(--color-result-detail-loader-borderleft); + } +} + +#results.image-detail-open.scrolling article.result-images[data-vim-selected] .detail { + top: 0; + + a.result-images-source img { + max-height: calc(100vh - 25rem); + } +} + +@media screen and (max-width: @tablet) { + #results.image-detail-open article.result-images[data-vim-selected] .detail { + top: 0; + .ltr-left(0); + + a.result-images-source { + display: flex; + flex-direction: column; + justify-content: center; + + img { + width: 100%; + max-height: calc(100vh - 24rem); + } + } + + a.result-detail-next { + .ltr-right(1rem); + } + } +} + +@media screen and (max-width: @phone) { + #results.image-detail-open article.result-images[data-vim-selected] .detail { + top: 0; + .ltr-left(0); + padding: 1rem; + + a.result-images-source img { + width: 100%; + max-height: calc(100vh - 20rem); + margin: 0; + } + + .result-images-labels p span { + width: inherit; + .ltr-margin-right(1rem); + } + } +} diff --git a/searx/static/themes/magi/src/less/embedded.less b/searx/static/themes/magi/src/less/embedded.less new file mode 100644 index 000000000..2e9216ad2 --- /dev/null +++ b/searx/static/themes/magi/src/less/embedded.less @@ -0,0 +1,33 @@ +iframe[src^="https://w.soundcloud.com"] { + height: 120px; +} + +iframe[src^="https://www.deezer.com"] { + // The real size is 92px, but 94px are needed to avoid an inner scrollbar of + // the embedded HTML. + height: 94px; +} + +iframe[src^="https://www.mixcloud.com"] { + // the embedded player from mixcloud has some quirks: initial there is an + // issue with an image URL that is blocked since it is an a Cross-Origin + // request. The alternative text ('; + } + html += ' '; // row + } + } + + html += ''; + + html += ' '; // col-sm-* + + if (!first || lastCategory) { + html += '' + cat[0].cat + '
'; + html += ''; + + for (var cj in cat) { + html += '
'; + html += '- ' + cat[cj].key + ' ' + cat[cj].des + '
'; + } + + html += 'then cause an + // scrollbar in the inner of the iframe we can't avoid. Another quirk comes + // when pressing the play button, somtimes the shown player has an height of + // 200px, somtimes 250px. + height: 250px; +} + +iframe[src^="https://bandcamp.com/EmbeddedPlayer"] { + // show playlist + height: 350px; +} + +iframe[src^="https://bandcamp.com/EmbeddedPlayer/track"] { + // hide playlist + height: 120px; +} + +iframe[src^="https://genius.com/songs"] { + height: 65px; +} diff --git a/searx/static/themes/magi/src/less/index.less b/searx/static/themes/magi/src/less/index.less new file mode 100644 index 000000000..afc6d4332 --- /dev/null +++ b/searx/static/themes/magi/src/less/index.less @@ -0,0 +1,52 @@ +#main_index { + margin-top: 26vh; +} + +.index { + text-align: center; + + .title { + background: url('../img/logo.png') no-repeat; + filter: var(--invert-logo); + min-height: 4rem; + margin: 4rem auto; + background-position: center; + background-size: contain; + } + + h1 { + font-size: 4em; + visibility: hidden; + } + + #search, + #search_header { + margin: 0 auto; + background: inherit; + border: inherit; + padding: 0; + display: block; + } + + .search_filters { + display: block; + margin: 1em 0; + } + + .category label { + padding: 6px 10px; + border-bottom: initial !important; + } +} + +@media screen and (max-width: @tablet) { + div.title { + h1 { + font-size: 1em; + } + } + + #main_index { + margin-top: 6em; + } +} diff --git a/searx/static/themes/magi/src/less/info.less b/searx/static/themes/magi/src/less/info.less new file mode 100644 index 000000000..8c83c379d --- /dev/null +++ b/searx/static/themes/magi/src/less/info.less @@ -0,0 +1,9 @@ +.info-page { + code { + font-family: monospace; + color: var(--color-doc-code); + background-color: var(--color-doc-code-background); + padding: 2px 5px; + .rounded-corners(5px); + } +} diff --git a/searx/static/themes/magi/src/less/mixins.less b/searx/static/themes/magi/src/less/mixins.less new file mode 100644 index 000000000..a4bae7128 --- /dev/null +++ b/searx/static/themes/magi/src/less/mixins.less @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +// Mixins +.text-size-adjust (@property: 100%) { + -webkit-text-size-adjust: @property; + -ms-text-size-adjust: @property; + -moz-text-size-adjust: @property; + text-size-adjust: @property; +} + +.rounded-corners (@radius: 10px) { + border-radius: @radius; +} + +.rounded-right-corners (@radius: 0 10px 10px 0) { + border-radius: @radius; +} + +.rounded-corners-tiny (@radius: 5px) { + border-radius: @radius; +} + +// disable user selection +.disable-user-select () { + -webkit-touch-callout: none; + user-select: none; +} + +.show-content-button() { + padding: 5px 10px; + .rounded-corners-tiny; + background: var(--color-show-btn-background); + color: var(--color-show-btn-font); + cursor: pointer; + + &:hover { + background: var(--color-btn-background); + color: var(--color-btn-font); + } +} diff --git a/searx/static/themes/magi/src/less/new_issue.less b/searx/static/themes/magi/src/less/new_issue.less new file mode 100644 index 000000000..a1e5006e3 --- /dev/null +++ b/searx/static/themes/magi/src/less/new_issue.less @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +.stats_endpoint { + .github-issue-button { + display: block; + padding: 8px 16px; + font-family: sans-serif; + font-size: 16px; + color: white; + background-color: #238636; + border: #2ea043; + border-radius: 10px !important; + box-shadow: rgba(0, 0, 0, 0) 0 0 0 0; + } + + .github-issue-button:hover { + background-color: #2ea043; + } + + .issue-hide { + display: none; + } + + input[type=checked] { + position: absolute; + } + + label { + margin: 1rem 1rem 1rem 0; + } + + .step_content { + margin: 1rem 1rem 1rem 2rem; + } + + .step1, + .step2 { + visibility: hidden; + } + + .step1_delay { + transition: visibility 0s linear 4s; + } + + #step1:checked ~ .step1, + #step2:checked ~ .step2 { + visibility: visible; + } +} diff --git a/searx/static/themes/magi/src/less/preferences.less b/searx/static/themes/magi/src/less/preferences.less new file mode 100644 index 000000000..6913bbb90 --- /dev/null +++ b/searx/static/themes/magi/src/less/preferences.less @@ -0,0 +1,196 @@ +#tab-content-query table td, +#tab-content-query table th { + .ltr-text-align-right() !important; + height: 3rem; +} + +#main_preferences { + form { + width: 100%; + } + + fieldset { + margin: 8px; + border: none; + } + + legend { + margin: 0; + padding: 5px 0 0 0; + display: block; + .ltr-float-left(); + width: 300px; + } + + .value { + margin: 0; + padding: 0; + .ltr-float-left(); + width: 15em; + + select, + input[type="text"] { + font-size: inherit !important; + margin-top: 0; + .ltr-margin-right(1rem); + margin-bottom: 0; + .ltr-margin-left(0); + } + + select { + width: 14rem; + } + + input[type="text"] { + width: 13.25rem; + color: var(--color-toolkit-input-text-font); + border: none; + background: none repeat scroll 0 0 var(--color-toolkit-select-background); + padding: 0.2rem 0.4rem; + height: 2rem; + .rounded-corners-tiny; + + &:hover, + &:focus { + background-color: var(--color-toolkit-select-background-hover); + } + } + + select:focus, + input:focus { + outline: none; + box-shadow: 0 0 1px 1px var(--color-btn-background); + } + } + + .description { + margin: 0; + padding: 5px 0 0 0; + .ltr-float-right(); + width: 50%; + color: var(--color-settings-engine-description-font); + font-size: 90%; + } + + table { + border-collapse: collapse; + } + + table td { + text-align: center; + } + + .category { + .ltr-margin-right(0.5rem); + + label { + border: 2px solid transparent; + padding: 0.2rem 0.4rem; + .rounded-corners-tiny; + } + } + + .category input[type="checkbox"]:checked + label { + border: 2px solid var(--color-categories-item-border-selected); + } + + table.table_engines { + td { + height: 3.75rem; + } + + th.name { + /* stylelint-disable */ + label { + cursor: pointer; + } + /* stylelint-enable */ + + .engine-tooltip { + margin-top: 1.8rem; + .ltr-left(calc((100% - 85em) / 2 + 10em)); + max-width: 40rem; + + .engine-description { + margin-top: 0.5rem; + } + } + } + + .engine-group { + .ltr-text-align-left(); + font-weight: normal; + background: var(--color-settings-engine-group-background); + } + + .name, + .shortcut { + .ltr-text-align-left(); + } + } + + table.cookies { + width: 100%; + direction: ltr; + + th, + td { + text-align: left; + font-family: monospace; + font-size: 1rem; + padding: 0.5em; + vertical-align: top; + } + + td:first-child { + word-break: keep-all; + width: 14rem; + padding-right: 1rem; + } + + td:last-child { + word-break: break-all; + } + + & > tbody > tr:nth-child(even) > th, + & > tbody > tr:nth-child(even) > td { + background-color: var(--color-settings-tr-hover); + } + } + + .preferences_back { + background: none repeat scroll 0 0 var(--color-btn-background); + color: var(--color-btn-font); + border: 0 none; + .rounded-corners; + + cursor: pointer; + display: inline-block; + margin: 2px 4px; + padding: 0.7em; + + a { + color: var(--color-settings-return-font); + } + + a::first-letter { + text-transform: uppercase; + } + } + + div.selectable_url { + pre { + width: 100%; + } + } +} + +@media screen and (max-width: @tablet) { + .preferences_back { + clear: both; + } + + .engine-tooltip { + .ltr-left(10em) !important; + } +} diff --git a/searx/static/themes/magi/src/less/result_templates.less b/searx/static/themes/magi/src/less/result_templates.less new file mode 100644 index 000000000..afabdf322 --- /dev/null +++ b/searx/static/themes/magi/src/less/result_templates.less @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +.osm-map-box { + height: 300px; + width: 100%; + margin: 10px 0; +} diff --git a/searx/static/themes/magi/src/less/search.less b/searx/static/themes/magi/src/less/search.less new file mode 100644 index 000000000..eff6a850d --- /dev/null +++ b/searx/static/themes/magi/src/less/search.less @@ -0,0 +1,341 @@ +/* +* SearXNG, A privacy-respecting, hackable metasearch engine +*/ + +#search { + padding: 0; + margin: 0; +} + +#search_header { + padding-top: 1.5em; + .ltr-padding-right(2em); + .ltr-padding-left(@results-offset - 3rem); + margin: 0; + background: var(--color-header-background); + border-bottom: 1px solid var(--color-header-border); + display: grid; + column-gap: 1.2rem; + row-gap: 1rem; + grid-template-columns: 3rem 1fr; + grid-template-areas: + "logo search" + "spacer categories"; +} + +.category { + display: inline-block; + position: relative; + .ltr-margin-right(1rem); + padding: 0; + + input { + display: none; + } + + label { + svg { + padding-right: 0.2rem; + } + + cursor: pointer; + padding: 0.2rem 0; + display: inline-flex; + text-transform: capitalize; + font-size: 0.9em; + border-bottom: 2px solid transparent; + .disable-user-select; + + div.category_name { + margin: auto 0; + } + } + + input[type="checkbox"]:checked + label { + color: var(--color-categories-item-selected-font); + border-bottom: 2px solid var(--color-categories-item-border-selected); + } +} + +#search_logo { + filter: var(--invert-logo); + grid-area: logo; + display: flex; + align-items: center; + justify-content: center; + + svg { + flex: 1; + width: 30px; + height: 30px; + margin: 0.5rem 0 auto 0; + } +} + +.search_categories { + grid-area: categories; + + .help { + display: none; + } + + &:hover .help { + display: block; + position: absolute; + background: var(--color-base-background); + padding: 1rem 0.6rem 0.6rem 0; + z-index: 1000; + width: 100%; + left: -0.1rem; + } +} + +#search_view { + grid-area: search; +} + +.search_box { + border-radius: 0.8rem; + width: @search-width; + display: inline-flex; + flex-direction: row; + white-space: nowrap; + box-shadow: var(--color-search-shadow); +} + +#clear_search { + display: block; + border-collapse: separate; + box-sizing: border-box; + width: 1.8rem; + margin: 0; + padding: 0.8rem 0.2rem; + background: none repeat scroll 0 0 var(--color-search-background); + border: none; + outline: none; + color: var(--color-search-font); + font-size: 1.1rem; + z-index: 10000; + + &:hover { + color: var(--color-search-background-hover); + } + + &.empty * { + display: none; + } +} + +html.no-js #clear_search.hide_if_nojs { + display: none; +} + +#q, +#send_search { + display: block; + margin: 0; + padding: 0.8rem; + background: none repeat scroll 0 0 var(--color-search-background); + border: none; + outline: none; + color: var(--color-search-font); + font-size: 1.1rem; + z-index: 2; +} + +#q { + width: 100%; + .ltr-padding-left(1rem); + .ltr-padding-right(0) !important; + .ltr-rounded-left-corners(0.8rem); +} + +#q::-ms-clear, +#q::-webkit-search-cancel-button { + display: none; +} + +#send_search { + .ltr-rounded-right-corners(0.8rem); + + &:hover { + cursor: pointer; + background: var(--color-search-background-hover); + color: var(--color-search-background); + } +} + +.no-js #clear_search, +.no-js #send_search { + width: auto !important; + .ltr-border-left(1px solid var(--color-search-border)); +} + +.search_filters { + margin-top: 0.6rem; + .ltr-margin-right(0); + margin-bottom: 0; + .ltr-margin-left(@results-offset + 0.6rem); + display: flex; + overflow-x: auto; + overscroll-behavior-inline: contain; + + select { + background-color: inherit; + + &:hover, + &:focus { + color: var(--color-base-font); + } + } +} + +@media screen and (max-width: @tablet) { + #search_header { + padding: 1.5em @results-tablet-offset 0 @results-tablet-offset; + column-gap: @results-tablet-offset; + } + + .search_filters { + margin-top: 0.6rem; + .ltr-margin-right(0); + margin-bottom: 0; + .ltr-margin-left(@results-tablet-offset + 3rem); + } + + #categories { + font-size: 90%; + clear: both; + + .checkbox_container { + margin: auto; + margin-top: 2px; + } + } +} + +@media screen and (max-width: @tablet) and (hover: none) { + #main_index, + #main_results { + #categories_container { + width: max-content; + + .category { + display: inline-block; + width: auto; + } + } + + #categories { + width: 100%; + .ltr-text-align-left(); + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + } + } +} + +@media screen and (max-width: @phone) { + #search_header { + width: 100%; + margin: 0; + padding: 0.1rem 0 0 0; + column-gap: 0; + row-gap: 0; + grid-template-areas: + "logo search" + "categories categories"; + } + + .search_logo { + padding: 0; + } + + .search_box { + width: 98%; + display: flex; + margin: 0 auto; + } + + #q { + width: 100%; + flex: 1; + } + + .search_filters { + margin: 0; + } + + .category { + display: inline-block; + width: auto; + margin: 0; + + label { + padding: 1rem !important; + margin: 0 !important; + + svg { + display: none; + } + } + } + + #search_view:focus-within { + display: block; + background-color: var(--color-search-background); + position: absolute; + top: 0; + height: 100%; + width: 100%; + z-index: 10000; + + .search_box { + border-bottom: 1px solid var(--color-search-border); + width: 100%; + border-radius: 0; + box-shadow: none; + + #send_search { + .ltr-margin-right(0) !important; // Delete when send_search button is disabled on mobile. + } + + * { + border: none; + border-radius: 0; + box-shadow: none; + } + } + } + + #main_results #q:placeholder-shown ~ #send_search { + .ltr-margin-right(2.6rem); + transition: margin 0.1s; + } +} + +@media screen and (max-width: @ultra-small-phone) { + #search_header { + grid-template-areas: + "search search" + "categories categories"; + } + + #search_logo { + display: none; + } +} + +#categories { + .disable-user-select; + + &::-webkit-scrollbar { + width: 0; + height: 0; + } +} + +#categories_container { + position: relative; +} diff --git a/searx/static/themes/magi/src/less/stats.less b/searx/static/themes/magi/src/less/stats.less new file mode 100644 index 000000000..4658d4f2b --- /dev/null +++ b/searx/static/themes/magi/src/less/stats.less @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +.engine-stats { + border-spacing: 0; + border-collapse: collapse; + + tr td, + tr th { + border-bottom: 1px solid var(--color-result-border); + padding: 0.25rem; + } + + table.engine-tooltip { + border-spacing: 0; + border-collapse: collapse; + + td, + th { + border: none; + } + } + + .engine-name { + width: 20rem; + } + + .engine-score { + width: 7rem; + text-align: right; + } + + .engine-reliability { + text-align: right; + } +} + +table.engine-error th.engine-error-type, +table.engine-error td.engine-error-type, +failed-test { + width: 10rem; +} + +.engine-errors { + margin-top: 3rem; + + table.engine-error { + max-width: 1280px; + margin: 1rem 0 3rem 0; + border: 1px solid var(--color-result-border); + .ltr-text-align-left(); + + tr th, + tr td { + padding: 0.5rem; + } + + & span.log_parameters { + border-right: 1px solid solid var(--color-result-border); + padding: 0 1rem 0 0; + margin: 0 0 0 0.5rem; + } + } +} + +.bar-chart-value { + width: 3em; + display: inline-block; + text-align: right; + padding-right: 0.5rem; +} + +.bar-chart-graph { + width: calc(100% - 5rem); + display: inline-block; +} + +.bar-chart-bar { + border: 3px solid #5bc0de; + margin: 1px 0; +} + +.bar-chart-serie1 { + border: 3px solid #5bc0de; + margin: 1px 0; + float: left; +} + +.bar-chart-serie2 { + border: 3px solid #deb15b; + margin: 1px 0; + float: left; +} + +.bar0 { + width: 0; + border: 0; +} + +.generate-bar(100); + +.generate-bar(@n, @i: 1) when (@i =< @n) { + .bar@{i} { + width: (@i * 100% / @n); + } + .generate-bar(@n, (@i + 1)); +} diff --git a/searx/static/themes/magi/src/less/style-center.less b/searx/static/themes/magi/src/less/style-center.less new file mode 100644 index 000000000..30c52cd5a --- /dev/null +++ b/searx/static/themes/magi/src/less/style-center.less @@ -0,0 +1,118 @@ +/* +--center-page-width overrides the less variable @results-width when the results are centered +see the CSS rules for #results in style.less ( grid-template-columns and gap). + +In this file, the --center-page-width values comes from the Oscar theme (Bootstrap 3). + +All rules starts with ".center-aligment-yes #main_results" to be enabled only +on the /search URL and when the "center alignment" preference is enabled. +*/ + +@media screen and (min-width: @phone) { + .center-aligment-yes #main_results { + --center-page-width: 48rem; + } +} + +@media screen and (min-width: 62rem) { + .center-aligment-yes #main_results { + --center-page-width: 60rem; + } +} + +@media screen and (min-width: @tablet) { + .center-aligment-yes #main_results { + --center-page-width: 73rem; + } +} + +@media screen and (min-width: @phone) and (max-width: @tablet) { + // any change must be reset in @media screen and (min-width: @tablet) { ... } + .center-aligment-yes #main_results { + #results { + grid-template-columns: 60% calc(40% - @results-gap); + margin-left: 0; + margin-right: 0; + } + + #urls { + .ltr-margin-left(3rem); + } + + #sidebar { + .ltr-margin-right(1rem); + } + + #backToTop { + .ltr-left(calc(60% + 1rem)); + } + } +} + +@media screen and (min-width: @tablet) { + .center-aligment-yes #main_results { + display: flex; + flex-direction: column; + align-items: center; + + #search { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + + #search_header { + grid-template-columns: calc(50% - 4.5rem - var(--center-page-width) / 2) 3rem var(--center-page-width); + grid-template-areas: "na logo search" "na spacer categories"; + column-gap: 1.2rem; // from search.less + width: 100%; + padding-left: 0; + padding-right: 0; + } + + .search_filters { + .ltr-margin-left(0.5rem); + width: var(--center-page-width); + } + + #results { + // from style.less (when screen width = @tablet, reset layout from tablet) + .ltr-margin-right(2rem); + .ltr-margin-left(@results-offset); + // + + &.only_template_images, + &.image-detail-open { + // * grid-template-columns and .ltr-margin-left are set in style.less + // * With .image-detail-open.only_template_images, the width is set in detail.less + // * #results is going to be centered because of the #main_results rules, + // align-self aligns the results on the left or right according to the language. + align-self: flex-start; + } + + &:not(.only_template_images):not(.image-detail-open) { + // the gap is set in style.less + .ltr-margin-left(1.5rem); + grid-template-columns: calc(var(--center-page-width) - @results-gap - @results-sidebar-width) @results-sidebar-width; + + #backToTop { + .ltr-left(calc(50% - @results-sidebar-width - @results-gap + 1rem + var(--center-page-width) / 2)); + } + } + + .result .content { + max-width: inherit; + } + } + + // from style.less (when screen width = @tablet, reset layout from tablet) + #urls { + .ltr-margin-left(0); + } + + #sidebar { + .ltr-margin-right(0); + } + } +} diff --git a/searx/static/themes/magi/src/less/style-ltr.less b/searx/static/themes/magi/src/less/style-ltr.less new file mode 100644 index 000000000..6f7218b02 --- /dev/null +++ b/searx/static/themes/magi/src/less/style-ltr.less @@ -0,0 +1,85 @@ +.ltr-left(@offset) { + left: @offset; +} + +.ltr-right(@offset) { + right: @offset; +} + +.ltr-margin-right(@offset) { + margin-right: @offset; +} + +.ltr-margin-left(@offset) { + margin-left: @offset; +} + +.ltr-border-right(@offset) { + border-right: @offset; +} + +.ltr-border-left(@offset) { + border-left: @offset; +} + +.ltr-padding-right(@offset) { + padding-right: @offset; +} + +.ltr-padding-left(@offset) { + padding-left: @offset; +} + +.ltr-float-left() { + float: left; +} + +.ltr-float-right() { + float: right; +} + +.ltr-text-align-right() { + text-align: right; +} + +.ltr-rounded-left-corners(@radius) { + border-radius: @radius 0 0 @radius; +} + +.ltr-rounded-top-left-corners(@radius) { + border-radius: @radius 0 0 0; +} + +.ltr-rounded-bottom-left-corners(@radius) { + border-radius: 0 0 0 @radius; +} + +.ltr-rounded-right-corners(@radius) { + border-radius: 0 @radius @radius 0; +} + +.ltr-rounded-top-right-corners(@radius) { + border-radius: 0 @radius 0 0; +} + +.ltr-rounded-bottom-right-corners(@radius) { + border-radius: 0 0 @radius 0; +} + +.ltr-text-align-left() { + text-align: left; +} + +.ltr-border-left-width(@offset) { + border-left-width: @offset; +} + +.ltr-border-right-width(@offset) { + border-right-width: @offset; +} + +.ltr-transform() { + transform: scale(1, 1); +} + +@import "style.less"; diff --git a/searx/static/themes/magi/src/less/style-rtl.less b/searx/static/themes/magi/src/less/style-rtl.less new file mode 100644 index 000000000..aa97e039c --- /dev/null +++ b/searx/static/themes/magi/src/less/style-rtl.less @@ -0,0 +1,155 @@ +.ltr-left(@offset) { + right: @offset; +} + +.ltr-right(@offset) { + left: @offset; +} + +.ltr-margin-right(@offset) { + margin-left: @offset; +} + +.ltr-margin-left(@offset) { + margin-right: @offset; +} + +.ltr-border-right(@offset) { + border-left: @offset; +} + +.ltr-border-left(@offset) { + border-right: @offset; +} + +.ltr-padding-right(@offset) { + padding-left: @offset; +} + +.ltr-padding-left(@offset) { + padding-right: @offset; +} + +.ltr-float-left() { + float: right; +} + +.ltr-float-right() { + float: left; +} + +.ltr-text-align-right() { + text-align: left; +} + +.ltr-rounded-left-corners(@radius) { + border-radius: 0 @radius @radius 0; +} + +.ltr-rounded-top-left-corners(@radius) { + border-radius: 0 @radius 0 0; +} + +.ltr-rounded-bottom-left-corners(@radius) { + border-radius: 0 0 @radius 0; +} + +.ltr-rounded-right-corners(@radius) { + border-radius: @radius 0 0 @radius; +} + +.ltr-rounded-top-right-corners(@radius) { + border-radius: @radius 0 0 0; +} + +.ltr-rounded-bottom-right-corners(@radius) { + border-radius: 0 0 0 @radius; +} + +.ltr-text-align-left() { + text-align: right; +} + +.ltr-border-left-width(@offset) { + border-right-width: @offset; +} + +.ltr-border-right-width(@offset) { + border-left-width: @offset; +} + +.ltr-transform() { + transform: scale(-1, 1); +} + +@import "style.less"; + +#q, +#sidebar .infobox dt bdi { + direction: rtl; +} + +// URL are displayed LTR but align on the right +#urls { + direction: initial; + text-align: right; + + .result .url_wrapper { + justify-content: end; + } +} + +// Image flexbox +#main_results div#results.only_template_images #urls { + direction: rtl; +} + +// Image detail +#results.image-detail-open article.result-images[data-vim-selected] .detail .result-images-labels p { + direction: rtl; + + &.result-url { + // Display URL using the LTR direction + direction: ltr; + + span { + // And put the label on the right + direction: rtl; + float: right; + } + } +} + +// select HTML element +@supports ((background-position-x: 100%) and ((appearance: none) or (-webkit-appearance: none) or (-moz-appearance: none))) { + select { + border-width: 0 0 0 2rem; + background-position-x: -2rem; + } +} + +// vim hotkey helps is not translated +#vim-hotkeys-help table { + direction: ltr; + text-align: left; +} + +// Logo on the right +#main_preferences h1, +#main_stats h1 { + background-position-x: 100%; +} + +// patch of stats.less +.bar-chart-serie1, +.bar-chart-serie2 { + float: right; +} + +.engine-stats .engine-name, +.engine-stats .engine-score, +.engine-stats .result-count, +.engine-stats .response-time, +.engine-stats .engine-reliability { + text-align: right; +} diff --git a/searx/static/themes/magi/src/less/style.less b/searx/static/themes/magi/src/less/style.less new file mode 100644 index 000000000..b4c3ce393 --- /dev/null +++ b/searx/static/themes/magi/src/less/style.less @@ -0,0 +1,1080 @@ +/* +* SearXNG, A privacy-respecting, hackable metasearch engine +* +* To convert "style.less" to "style.css" run: $make styles +*/ + +// stylelint-disable no-descending-specificity + +@import (inline) "../../node_modules/normalize.css/normalize.css"; +@import "definitions.less"; +@import "mixins.less"; +@import "code.less"; +@import "toolkit.less"; +@import "autocomplete.less"; +@import "detail.less"; +@import "animations.less"; +@import "embedded.less"; +@import "info.less"; +@import "new_issue.less"; +@import "stats.less"; +@import "result_templates.less"; + +// for index.html template +@import "index.less"; + +// for preferences.html template +@import "preferences.less"; + +// Search-Field +@import "search.less"; + +// to center the results +@import "style-center.less"; + +// ion-icon +.ion-icon { + display: inline-block; + vertical-align: bottom; + line-height: 1; + text-decoration: inherit; + .ltr-transform(); +} + +.ion-icon-small { + width: 1rem; + height: 1rem; + .ion-icon; +} + +.ion-icon-big { + width: 1.5rem; + height: 1.5rem; + .ion-icon; +} + +// Main LESS-Code +html { + font-family: sans-serif; + font-size: 0.9em; + .text-size-adjust; + + color: var(--color-base-font); + background-color: var(--color-base-background); + padding: 0; + margin: 0; +} + +body, +main { + padding: 0; + margin: 0; +} + +body { + display: flex; + flex-direction: column; + height: 100vh; + margin: 0; +} + +main { + width: 100%; + margin-bottom: 2rem; + flex: 1; +} + +.page_with_header { + margin: 2em auto; + width: 85em; +} + +footer { + clear: both; + min-height: 4rem; + padding: 1rem 0; + width: 100%; + text-align: center; + background-color: var(--color-footer-background); + border-top: 1px solid var(--color-footer-border); + overflow: hidden; + + p { + font-size: 0.9em; + } +} + +.page_with_header .logo { + height: 40px; + filter: var(--invert-logo); +} + +input[type="submit"], +#results button[type="submit"] { + padding: 0.7rem; + display: inline-block; + background: var(--color-btn-background); + color: var(--color-btn-font); + .rounded-corners; + + border: 0; + cursor: pointer; +} + +a { + text-decoration: none; + color: var(--color-url-font); + + &:visited { + color: var(--color-url-visited-font); + + .highlight { + color: var(--color-url-visited-font); + } + } +} + +article[data-vim-selected] { + background: var(--color-result-vim-selected); + .ltr-border-left(0.2rem solid var(--color-result-vim-arrow)); + .ltr-rounded-right-corners(10px); +} + +article.result-images[data-vim-selected] { + background: var(--color-result-vim-arrow); + border: none; + .rounded-corners; + + .image_thumbnail { + filter: opacity(60%); + } + + span.title, + span.source { + color: var(--color-result-image-span-font-selected); + } +} + +article[data-vim-selected].category-videos, +article[data-vim-selected].category-news, +article[data-vim-selected].category-map, +article[data-vim-selected].category-music, +article[data-vim-selected].category-files, +article[data-vim-selected].category-social { + border: 1px solid var(--color-result-vim-arrow); + .rounded-corners; +} + +.result { + margin: @results-margin 0; + padding: @result-padding; + .ltr-border-left(0.2rem solid transparent); + + h3 { + font-size: 1.2rem; + word-wrap: break-word; + margin: 0.4rem 0 0.4rem 0; + padding: 0; + + a { + color: var(--color-result-link-font); + font-weight: normal; + font-size: 1.1em; + + &:visited { + color: var(--color-result-link-visited-font); + } + + &:focus, + &:hover { + text-decoration: underline; + border: none; + outline: none; + } + } + } + + .cache_link, + .proxyfied_link { + font-size: 0.9em !important; + } + + .content, + .stat { + font-size: 0.9em; + margin: 0; + padding: 0; + max-width: 54em; + word-wrap: break-word; + line-height: 1.24; + + .highlight { + color: var(--color-result-description-highlight-font); + background: inherit; + font-weight: bold; + } + } + + .altlink a { + font-size: 0.9em; + margin: 0 10px 0 0; + .show-content-button; + } + + .codelines { + .highlight { + color: inherit; + background: inherit; + font-weight: normal; + } + } + + .url_wrapper { + display: flex; + font-size: 1rem; + color: var(--color-result-url-font); + flex-wrap: nowrap; + overflow: hidden; + flex-direction: row; + margin: 0; + padding: 0; + + .url_o1 { + white-space: nowrap; + flex-shrink: 1; + } + + .url_o1::after { + content: " "; + width: 1ch; + display: inline-block; + } + + .url_o2 { + overflow: hidden; + white-space: nowrap; + flex-basis: content; + flex-grow: 0; + flex-shrink: 1; + text-align: right; + + .url_i2 { + float: right; + } + } + } + + .published_date, + .result_length, + .result_author, + .result_shipping, + .result_source_country { + font-size: 0.8em; + color: var(--color-result-publishdate-font); + } + + .result_price { + font-size: 1.2em; + color: var(--color-result-description-highlight-font); + } + + img { + &.thumbnail { + .ltr-float-left(); + padding-top: 0.6rem; + .ltr-padding-right(1rem); + width: 20rem; + height: unset; // remove heigth value that was needed for lazy loading + } + + &.image { + .ltr-float-left(); + padding-top: 0.6rem; + .ltr-padding-right(1rem); + width: 7rem; + max-height: 7rem; + object-fit: scale-down; + object-position: right top; + } + } + + .break { + clear: both; + } +} + +.result-paper { + .attributes { + display: table; + border-spacing: 0.125rem; + + div { + display: table-row; + + span { + font-size: 0.9rem; + margin-top: 0.25rem; + display: table-cell; + + time { + font-size: 0.9rem; + } + } + + span:first-child { + color: var(--color-base-font); + min-width: 10rem; + } + + span:nth-child(2) { + color: var(--color-result-publishdate-font); + } + } + } + + .content { + margin-top: 0.25rem; + } + + .comments { + font-size: 0.9rem; + margin: 0.25rem 0 0 0; + padding: 0; + word-wrap: break-word; + line-height: 1.24; + font-style: italic; + } +} + +.template_group_images { + display: flex; + flex-wrap: wrap; +} + +.template_group_images::after { + flex-grow: 10; + content: ""; +} + +.category-videos, +.category-news, +.category-map, +.category-music, +.category-files, +.category-social { + border: 1px solid var(--color-result-border); + margin: 0 @results-tablet-offset 1rem @results-tablet-offset !important; + .rounded-corners; +} + +.category-social .image { + width: auto !important; + min-width: 48px; + min-height: 48px; + padding: 0 5px 25px 0 !important; +} + +.audio-control audio { + width: 100%; + padding: 10px 0 0 0; +} + +.embedded-content iframe { + width: 100%; + padding: 10px 0 0 0; +} + +.result-videos .content { + overflow: hidden; +} + +.result-videos .embedded-video iframe { + width: 100%; + aspect-ratio: 16 / 9; + padding: 10px 0 0 0; +} + +@supports not (aspect-ratio: 1 / 1) { + // support older browsers which do not have aspect-ratio + // https://caniuse.com/?search=aspect-ratio + .result-videos .embedded-video iframe { + height: calc(@results-width * 9 / 16); + } +} + +.engines { + .ltr-float-right(); + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + color: var(--color-result-engines-font); + + span { + font-size: smaller; + margin-top: 0; + margin-bottom: 0; + .ltr-margin-right(0.5rem); + .ltr-margin-left(0); + } +} + +.small_font { + font-size: 0.8em; +} + +.highlight { + color: var(--color-result-link-font-highlight); + background: inherit; +} + +.empty_element { + font-style: italic; +} + +.result-images { + flex-grow: 1; + padding: 0.5rem 0.5rem 3rem 0.5rem; + margin: 0.25rem; + border: none !important; + height: @results-image-row-height; + + & > a { + position: relative; + } + + img { + margin: 0; + padding: 0; + border: none; + height: 100%; + width: 100%; + object-fit: cover; + vertical-align: bottom; + background: var(--color-result-image-background); + } + + span.title, + span.source { + display: block; + position: absolute; + + width: 100%; + font-size: 0.9rem; + color: var(--color-result-image-span-font); + padding: 0.5rem 0 0 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + span.source { + padding: 1.8rem 0 0 0; + font-size: 0.7rem; + } +} + +.result-map { + img.image { + .ltr-float-right() !important; + height: 100px !important; + width: auto !important; + } + + table { + font-size: 0.9em; + width: auto; + border-collapse: separate; + border-spacing: 0 0.35rem; + + th { + font-weight: inherit; + width: 17rem; + vertical-align: top; + .ltr-text-align-left(); + } + + td { + vertical-align: top; + .ltr-text-align-left(); + } + } +} + +.hidden { + display: none !important; +} + +#results { + margin-top: 1rem; + .ltr-margin-right(2rem); + margin-bottom: 0; + .ltr-margin-left(@results-offset); + display: grid; + grid-template-columns: @results-width @results-sidebar-width; + grid-template-rows: min-content min-content 1fr min-content; + gap: 0 @results-gap; + grid-template-areas: + "corrections sidebar" + "answers sidebar" + "urls sidebar" + "pagination sidebar"; +} + +#results #sidebar *:first-child, +#results #urls *:first-child { + margin-top: 0; +} + +#urls { + padding: 0; + grid-area: urls; +} + +#suggestions { + .wrapper { + display: flex; + flex-flow: column; + justify-content: flex-end; + flex-wrap: wrap; + flex-direction: row; + + form { + display: inline-block; + flex: 1 1 50%; + } + } +} + +#suggestions, +#infoboxes { + input { + padding: 0; + margin: 3px; + font-size: 0.9em; + display: inline-block; + background: transparent; + color: var(--color-result-search-url-font); + cursor: pointer; + width: calc(100%); + text-overflow: ellipsis; + overflow: hidden; + text-align: left; + } + + input[type="submit"], + .infobox .url a { + color: var(--color-result-link-font); + text-decoration: none; + font-size: 0.9rem; + + &:hover { + text-decoration: underline; + } + } +} + +#corrections { + grid-area: corrections; + display: flex; + flex-flow: row wrap; + margin: 0 0 1em 0; + + h4, + input[type="submit"] { + display: inline-block; + padding: 0.5rem; + margin: 0.5rem; + } + + input[type="submit"] { + font-size: 0.8rem; + .rounded-corners-tiny; + } +} + +#suggestions .title, +#search_url .title, +#apis .title { + margin: 2em 0 0.5em 0; + color: var(--color-base-font); +} + +#answers { + grid-area: answers; + background: var(--color-answer-background); + padding: @result-padding; + margin: 1rem 0; + margin-top: 0; + color: var(--color-answer-font); + .rounded-corners; + + h4 { + display: none; + } + + span { + overflow-wrap: anywhere; + } +} + +#infoboxes { + form { + min-width: 210px; + } +} + +#sidebar { + grid-area: sidebar; + word-wrap: break-word; + color: var(--color-sidebar-font); + + .infobox { + margin: 10px 0 10px; + border: 1px solid var(--color-sidebar-border); + padding: 1rem; + font-size: 0.9em; + .rounded-corners; + + h2 { + margin: 0 0 0.5em 0; + } + + img { + max-width: 100%; + max-height: 12em; + display: block; + margin: 0 auto; + padding: 0; + } + + dt { + font-weight: bold; + } + + .attributes { + dl { + margin: 0.5em 0; + } + + dt { + display: inline; + margin-top: 0.5em; + .ltr-margin-right(0.25em); + margin-bottom: 0.5em; + .ltr-margin-left(0); + padding: 0; + } + + dd { + display: inline; + margin: 0.5em 0; + padding: 0; + } + } + + input { + font-size: 1em; + } + + br { + clear: both; + } + + .attributes, + .urls { + clear: both; + } + } +} + +#search_url { + margin-top: 8px; + + div.selectable_url { + pre { + width: 200em; + } + } +} + +#links_on_top { + position: absolute; + .ltr-right(1.8rem); + .ltr-text-align-right(); + top: 2.2rem; + padding: 0; + border: 0; + display: flex; + align-items: center; + font-size: 1em; + color: var(--color-search-font); + + a { + display: flex; + align-items: center; + margin-left: 1em; + + svg { + font-size: 1.2em; + .ltr-margin-right(0.125em); + } + } + + a, + a:link *, + a:hover *, + a:visited *, + a:active * { + color: var(--color-search-font); + } +} + +#pagination { + grid-area: pagination; + + br { + clear: both; + } +} + +.numbered_pagination { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + overflow: hidden; +} + +.page_number { + background: transparent !important; + color: var(--color-result-link-font) !important; + text-decoration: underline; +} + +.page_number_current { + background: transparent; + color: var(--color-result-link-visited-font); + border: none; +} + +#apis { + margin-top: 8px; + clear: both; +} + +#backToTop { + border: 1px solid var(--color-backtotop-border); + margin: 0; + padding: 0; + font-size: 1em; + background: var(--color-backtotop-background); + position: fixed; + bottom: 8rem; + .ltr-left(@results-width + @results-offset + (0.5 * @results-gap - 1.2em)); + transition: opacity 0.5s; + opacity: 0; + .rounded-corners; + + a { + display: block; + margin: 0; + padding: 0.7em; + } + + a, + a:visited, + a:hover, + a:active { + color: var(--color-backtotop-font); + } +} + +#results.scrolling #backToTop { + opacity: 1; +} + +/* + tablet layout +*/ + +.results-tablet() { + #links_on_top { + span { + display: none; + } + } + + .page_with_header { + margin: 2rem 0.5rem; + width: auto; + } + + #infoboxes { + position: inherit; + max-width: inherit; + + .infobox { + clear: both; + + img { + .ltr-float-left(); + max-width: 10em; + margin-top: 0.5em; + .ltr-margin-right(0.5em); + margin-bottom: 0.5em; + .ltr-margin-left(0); + } + } + } + + #sidebar { + margin: 0 @results-tablet-offset @results-margin @results-tablet-offset; + padding: 0; + float: none; + border: none; + width: auto; + + input { + border: 0; + } + } + + #apis { + display: none; + } + + #search_url { + display: none; + } + + .result { + .thumbnail { + max-width: 98%; + } + + .url { + span.url { + display: block; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + width: 100%; + } + } + + .engines { + .ltr-float-right(); + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + padding: 0 0 3px 0; + } + } + + .result-images { + border-bottom: none !important; + } + + .image_result { + max-width: 98%; + + img { + max-width: 98%; + } + } + + #backToTop { + display: none; + } + + #pagination { + margin: 2rem 0 0 0 !important; + } + + #main_results div#results { + margin: 1rem auto 0 auto; + justify-content: center; + display: grid; + grid-template-columns: @results-width; + grid-template-rows: min-content min-content min-content 1fr min-content min-content; + gap: 0; + grid-template-areas: + "corrections" + "answers" + "sidebar" + "urls" + "pagination"; + } +} + +@media screen and (max-width: calc(@tablet - 0.5px)) { + #links_on_top { + span { + display: none; + } + } +} + +@media screen and (max-width: 52rem) { + body.results_endpoint { + #links_on_top { + .link_on_top_about, + .link_on_top_donate { + display: none; + } + } + } +} + +@media screen and (min-width: @phone) and (max-width: @tablet) { + // when .center-aligment-yes, see style-center.less + // the media query includes "min-width: @phone" + // because the phone layout includes the tablet layout unconditionally. + .center-aligment-no { + .results-tablet(); + } +} + +/* Misc */ + +#main_results div#results.only_template_images { + margin: 1rem @results-tablet-offset 0 @results-tablet-offset; + display: grid; + grid-template-columns: 100%; + grid-template-rows: min-content min-content min-content 1fr min-content; + gap: 0; + grid-template-areas: + "corrections" + "answers" + "sidebar" + "urls" + "pagination"; + + #sidebar { + display: none; + } + + #urls { + margin: 0; + display: flex; + flex-wrap: wrap; + } + + #urls::after { + flex-grow: 10; + content: ""; + } + + #backToTop { + .ltr-left(auto); + .ltr-right(1rem); + } + + #pagination { + .ltr-margin-right(4rem); + } +} + +/* + phone layout +*/ + +@media screen and (max-width: @phone) { + // based on the tablet layout + .results-tablet(); + + html { + background-color: var(--color-base-background-mobile); + } + + #main_results div#results { + grid-template-columns: 100%; + margin: 1rem 0 0 0; + } + + #links_on_top { + top: 0.8rem; + .ltr-right(0.7rem); + } + + #main_index #links_on_top { + top: 0.5rem; + .ltr-right(0.5rem); + } + + #results { + margin: 0; + padding: 0; + } + + #pagination { + margin: 2rem 1rem 0 1rem !important; + } + + article[data-vim-selected] { + border: 1px solid var(--color-result-vim-arrow); + .rounded-corners; + } + + .result { + background: var(--color-result-background); + margin: 1rem 0; + } + + .result-images { + margin: 0; + height: @results-image-row-height-phone; + background: var(--color-base-background-mobile); + } + + .infobox { + border: none !important; + background-color: var(--color-sidebar-background); + } + + .numbered_pagination { + display: none; + } + + .result-paper { + .attributes { + display: block; + + div { + display: block; + + span { + display: inline; + } + + span:first-child { + font-weight: bold; + } + + span:nth-child(2) { + .ltr-margin-left(0.5rem); + } + } + } + } +} + +/* + small-phone layout +*/ + +@media screen and (max-width: @small-phone) { + .result-videos { + img.thumbnail { + float: none !important; + } + + .content { + overflow: inherit; + } + } +} + +pre code { + white-space: pre-wrap; +} diff --git a/searx/static/themes/magi/src/less/toolkit.less b/searx/static/themes/magi/src/less/toolkit.less new file mode 100644 index 000000000..93fa5e04c --- /dev/null +++ b/searx/static/themes/magi/src/less/toolkit.less @@ -0,0 +1,639 @@ +// other solution : http://stackoverflow.com/questions/1577598/how-to-hide-parts-of-html-when-javascript-is-disabled/13857783#13857783 + +// stylelint-disable no-descending-specificity + +html.no-js .hide_if_nojs { + display: none; +} + +html.js .show_if_nojs { + display: none; +} + +.center { + text-align: center; +} + +.right { + float: right; +} + +.left { + float: left; +} + +.invisible { + display: none !important; +} + +.list-unstyled { + list-style-type: none; + + li { + margin-top: 4px; + margin-bottom: 4px; + } +} + +.danger { + background-color: var(--color-error-background); +} + +.warning { + background: var(--color-warning-background); +} + +.success { + background: var(--color-success-background); +} + +.badge { + display: inline-block; + color: var(--color-toolkit-badge-font); + background-color: var(--color-toolkit-badge-background); + text-align: center; + white-space: nowrap; + vertical-align: baseline; + min-width: 10px; + padding: 1px 5px; + border-radius: 5px; +} + +// kbd +kbd { + padding: 2px 4px; + margin: 1px; + font-size: 90%; + color: var(--color-toolkit-kbd-font); + background: var(--color-toolkit-kbd-background); +} + +// table +table { + width: 100%; + + &.striped { + tr { + border-bottom: 1px solid var(--color-settings-tr-hover); + } + } +} + +th { + padding: 0.4em; +} + +td { + padding: 0 4px; +} + +tr { + &:hover { + background: var(--color-settings-tr-hover) !important; + } +} + +// pre +.pre() { + display: block; + font-size: 0.8em; + word-break: break-all; + margin: 0.1em; + user-select: all; +} + +div.selectable_url { + display: block; + border: 1px solid var(--color-result-search-url-border); + padding: 4px; + color: var(--color-result-search-url-font); + margin: 0.1em; + overflow: hidden; + height: 1.2em; + line-height: 1.2em; + .rounded-corners-tiny; + + pre { + .pre(); + } +} + +// dialog +.dialog() { + position: relative; + display: flex; + padding: 1rem; + margin: 0 0 1em 0; + border: 1px solid var(--color-toolkit-dialog-border); + .ltr-text-align-left(); + .rounded-corners; + + .close { + .ltr-float-right(); + position: relative; + top: -3px; + color: inherit; + font-size: 1.5em; + } + + ul, + ol, + p { + margin: 1px 0 0 0; + } + + table { + width: auto; + } + + tr { + vertical-align: text-top; + + &:hover { + background: transparent !important; + } + } + + td { + padding: 0 1em 0 0; + padding-top: 0; + .ltr-padding-right(1rem); + padding-bottom: 0; + .ltr-padding-left(0); + } + + h4 { + margin-top: 0.3em; + margin-bottom: 0.3em; + } +} + +.dialog-error { + .dialog(); + + color: var(--color-error); + background: var(--color-error-background); + border-color: var(--color-error); +} + +.dialog-warning { + .dialog(); + + color: var(--color-warning); + background: var(--color-warning-background); + border-color: var(--color-warning); +} + +.dialog-modal { + .dialog(); + + display: block; + background: var(--color-toolkit-dialog-background); + position: fixed; + top: 50%; + left: 50%; + margin: 0 auto; + transform: translate(-50%, -50%); + z-index: 10000000; + + h3 { + margin-top: 0; + } +} + +// btn-collapse +.btn-collapse { + cursor: pointer; +} + +// +.scrollx { + overflow-x: auto; + overflow-y: hidden; + display: block; + padding: 0; + margin: 0; + border: none; +} + +/* -- tabs -- */ +.tabs .tabs > label { + font-size: 90%; +} + +ul.tabs { + border-bottom: 1px solid var(--color-toolkit-tabs-section-border); + list-style: none; + padding-left: 0; + + li { + display: flex; + } +} + +.tabs { + display: flex; + flex-wrap: wrap; + width: 100%; + min-width: 100%; + + & > * { + order: 2; + } + + & > input[type=radio] { + display: none; + } + + & > label, + & > li > a { + order: 1; + padding: 0.7em; + margin: 0 0.7em; + letter-spacing: 0.5px; + text-transform: uppercase; + border: solid var(--color-toolkit-tabs-label-border); + border-width: 0 0 2px 0; + color: unset; + + .disable-user-select(); + + cursor: pointer; + + &.active { + border-bottom: 2px solid var(--color-categories-item-border-selected); + background: var(--color-categories-item-selected); + color: var(--color-categories-item-selected-font); + } + } + + & > label:hover, + & > li > a:hover { + border-bottom: 2px solid var(--color-categories-item-border-selected); + } + + & > section { + min-width: 100%; + padding: 0.7rem 0; + box-sizing: border-box; + border-top: 1px solid var(--color-toolkit-tabs-section-border); + display: none; + } + + // default selection + & > label:last-of-type { + border-bottom: 2px solid var(--color-categories-item-border-selected); + background: var(--color-categories-item-selected); + color: var(--color-categories-item-selected-font); + letter-spacing: -0.1px; + } + + & > section:last-of-type { + display: block; + } +} + +html body .tabs > input:checked { + ~ section { + display: none; + } + + ~ label { + position: inherited; + background: inherit; + border-bottom: 2px solid transparent; + font-weight: normal; + color: inherit; + + &:hover { + border-bottom: 2px solid var(--color-categories-item-border-selected); + } + } + + + label { + border-bottom: 2px solid var(--color-categories-item-border-selected); + background: var(--color-categories-item-selected); + color: var(--color-categories-item-selected-font); + } + + + label + section { + display: block; + } +} + +/* -- select -- */ +select { + height: 2.4rem; + margin-top: 0; + .ltr-margin-right(1rem); + margin-bottom: 0; + .ltr-margin-left(0); + padding: 0.2rem !important; + color: var(--color-search-font); + font-size: 0.9rem; + z-index: 2; + + &:hover, + &:focus { + cursor: pointer; + } +} + +@supports ((background-position-x: 100%) and ((appearance: none) or (-webkit-appearance: none) or (-moz-appearance: none))) { + select { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + border-width: 0 2rem 0 0; + border-color: transparent; + background: data-uri('image/svg+xml;charset=UTF-8', @select-light-svg-path) no-repeat; + background-position-x: calc(100% + 2rem); + background-size: 2rem; + background-origin: content-box; + background-color: var(--color-toolkit-select-background); + outline: medium none; + text-overflow: ellipsis; + .rounded-corners-tiny; + + &:hover, + &:focus { + background-color: var(--color-toolkit-select-background-hover); + } + + option { + background-color: var(--color-base-background); + } + } + + @media (prefers-color-scheme: dark) { + html.theme-auto select, + html.theme-dark select { + background-image: data-uri('image/svg+xml;charset=UTF-8', @select-dark-svg-path); + } + } + + html.theme-dark select { + background-image: data-uri('image/svg+xml;charset=UTF-8', @select-dark-svg-path); + } +} + +/* -- checkbox-onoff -- */ +input.checkbox-onoff[type="checkbox"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + cursor: pointer; + display: inline-block; + width: 2.5em; + height: 0.7em; + box-shadow: none !important; + margin: 0 16px; + border-radius: 10px; + position: relative; + + /* focus & hover */ + &:focus, + &:hover { + outline: none; + } + + &:focus::after { + content: ""; + position: absolute; + width: 3.5em; + height: 1.65em; + border: 1px solid var(--color-btn-background); + border-radius: 12px; + box-shadow: var(--color-btn-background) 0 0 3px; + z-index: 10000; + top: -0.55em; + left: -0.6em; + } + + &::before { + position: absolute; + top: -0.5em; + display: flex; + justify-content: center; + align-items: center; + font-size: 0.75em; + width: 1.875em; + height: 1.875em; + border-radius: 50%; + } +} + +/* check mark +reversed-checkbox displays unchecked checkedboxes as checked, and vice versa. +see https://github.com/searxng/searxng/blob/3408d061aab9abc6168fec9bbc6deab71b236dac/searx/templates/simple/preferences.html#L313 +*/ +input.checkbox-onoff[type="checkbox"], +.reversed-checkbox input.checkbox-onoff[type="checkbox"]:checked { + background: var(--color-toolkit-checkbox-onoff-off-background); + + &::before { + left: -0.5em; + content: "\2715"; + color: var(--color-toolkit-checkbox-onoff-off-mark-color); + background: var(--color-toolkit-checkbox-onoff-off-mark-background); + } +} + +input.checkbox-onoff[type="checkbox"]:checked, +.reversed-checkbox input.checkbox-onoff[type="checkbox"] { + background: var(--color-toolkit-checkbox-onoff-on-background); + + &::before { + left: calc(100% - 1.5em); + content: "\2713"; + color: var(--color-toolkit-checkbox-onoff-on-mark-color); + background: var(--color-toolkit-checkbox-onoff-on-mark-background); + } +} + +/* -- checkbox -- */ +@supports (transform: rotate(-45deg)) { + input[type=checkbox]:not(.checkbox-onoff) { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + width: 20px; + height: 20px; + cursor: pointer; + position: relative; + top: 0; + left: 0; + border: 2px solid var(--color-toolkit-checkbox-input-border); + .rounded-corners(0.3em); + + &::after { + content: ''; + width: 9px; + height: 5px; + position: absolute; + top: 3px; + left: 2px; + border: 3px solid var(--color-toolkit-checkbox-label-border); + border-top: none; + border-right: none; + background: transparent; + opacity: 0; + transform: rotate(-45deg); + } + + &:checked::after { + border-color: var(--color-toolkit-checkbox-input-border); + opacity: 1; + } + } + + // disabled : can't be focused, show only the check mark + input[type=checkbox][disabled]:not(.checkbox-onoff) { + border: inherit; + background-color: transparent !important; + cursor: inherit; + } + + // if not checked and possible to checked then display a "light" check mark on hover + input.checkbox[type=checkbox]:not(:checked):not([disabled]):not(.checkbox-onoff):hover::after { + opacity: 0.5; + } +} + +@media screen and (max-width: @phone) { + .tabs > label { + width: 100%; + } +} + +/* -- loader -- */ +.loader, +.loader::after { + border-radius: 50%; + width: 2em; + height: 2em; +} + +.loader { + margin: 1em auto; + font-size: 10px; + position: relative; + text-indent: -9999em; + border-top: 0.5em solid var(--color-toolkit-loader-border); + border-right: 0.5em solid var(--color-toolkit-loader-border); + border-bottom: 0.5em solid var(--color-toolkit-loader-border); + border-left: 0.5em solid var(--color-toolkit-loader-borderleft); + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); + -webkit-animation: load8 1.2s infinite linear; + animation: load8 1.2s infinite linear; +} + +@-webkit-keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +/* -- engine-tooltip -- */ +.engine-tooltip { + display: none; + position: absolute; + padding: 0.5rem 1rem; + margin: 0 0 0 2rem; + border: 1px solid var(--color-toolkit-engine-tooltip-border); + background: var(--color-toolkit-engine-tooltip-background); + font-size: 14px; + font-weight: normal; + z-index: 1000000; + text-align: left; + .rounded-corners; +} + +th:hover .engine-tooltip, +td:hover .engine-tooltip, +.engine-tooltip:hover { + display: inline-block; +} + +/* -- stacked bar chart -- */ +.stacked-bar-chart { + margin: 0; + padding: 0 0.125rem 0 4rem; + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + width: fill; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + display: inline-flex; +} + +.stacked-bar-chart-value { + width: 3rem; + display: inline-block; + position: absolute; + padding: 0 0.5rem; + text-align: right; +} + +.stacked-bar-chart-base { + display: flex; + flex-shrink: 0; + flex-grow: 0; + flex-basis: unset; +} + +.stacked-bar-chart-median { + .stacked-bar-chart-base(); + + background: var(--color-base-font); + border: 1px solid rgba(var(--color-base-font-rgb), 0.9); + padding: 0.3rem 0; +} + +.stacked-bar-chart-rate80 { + .stacked-bar-chart-base(); + + background: transparent; + border: 1px solid rgba(var(--color-base-font-rgb), 0.3); + padding: 0.3rem 0; +} + +.stacked-bar-chart-rate95 { + .stacked-bar-chart-base(); + + background: transparent; + border-bottom: 1px dotted rgba(var(--color-base-font-rgb), 0.5); + padding: 0; +} + +.stacked-bar-chart-rate100 { + .stacked-bar-chart-base(); + + background: transparent; + border-left: 1px solid rgba(var(--color-base-font-rgb), 0.9); + padding: 0.4rem 0; + width: 1px; +} diff --git a/searx/static/themes/magi/src/less/toolkit_loader.less b/searx/static/themes/magi/src/less/toolkit_loader.less new file mode 100644 index 000000000..d426035a3 --- /dev/null +++ b/searx/static/themes/magi/src/less/toolkit_loader.less @@ -0,0 +1,46 @@ +.loader, +.loader::after { + border-radius: 50%; + width: 10em; + height: 10em; +} + +.loader { + margin: 60px auto; + font-size: 10px; + position: relative; + text-indent: -9999em; + border-top: 1.1em solid rgba(255, 255, 255, 0.2); + border-right: 1.1em solid rgba(255, 255, 255, 0.2); + border-bottom: 1.1em solid rgba(255, 255, 255, 0.2); + border-left: 1.1em solid #fff; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); + -webkit-animation: load8 1.1s infinite linear; + animation: load8 1.1s infinite linear; +} + +@-webkit-keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} diff --git a/searx/static/themes/magi/src/svg/information-circle-outline.svg b/searx/static/themes/magi/src/svg/information-circle-outline.svg new file mode 100644 index 000000000..b7c300094 --- /dev/null +++ b/searx/static/themes/magi/src/svg/information-circle-outline.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/searx/static/themes/magi/src/svg/select-dark.svg b/searx/static/themes/magi/src/svg/select-dark.svg new file mode 100644 index 000000000..97335cea5 --- /dev/null +++ b/searx/static/themes/magi/src/svg/select-dark.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/searx/static/themes/magi/src/svg/select-light.svg b/searx/static/themes/magi/src/svg/select-light.svg new file mode 100644 index 000000000..3b707f018 --- /dev/null +++ b/searx/static/themes/magi/src/svg/select-light.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/searx/static/themes/magi/svg4favicon.svgo.js b/searx/static/themes/magi/svg4favicon.svgo.js new file mode 100644 index 000000000..b9dd0da8e --- /dev/null +++ b/searx/static/themes/magi/svg4favicon.svgo.js @@ -0,0 +1,16 @@ +/** + * @license + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * svgo config: Optimize SVG for WEB usage + */ + +module.exports = { + plugins: [ + { + name: 'preset-default', + }, + // make diff friendly + 'sortAttrs', + ], +}; diff --git a/searx/static/themes/magi/svg4web.svgo.js b/searx/static/themes/magi/svg4web.svgo.js new file mode 100644 index 000000000..83f74cde1 --- /dev/null +++ b/searx/static/themes/magi/svg4web.svgo.js @@ -0,0 +1,19 @@ +/** + * @license + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * svgo config: Optimize SVG for WEB usage + */ + +module.exports = { + plugins: [ + { + name: 'preset-default', + }, + // make diff friendly + 'sortAttrs', + // Optimize SVG for WEB usage + 'convertStyleToAttrs', + 'removeXMLNS' + ], +}; diff --git a/searx/templates/magi/base.html b/searx/templates/magi/base.html index 0efc5a99c..1600783ed 100644 --- a/searx/templates/magi/base.html +++ b/searx/templates/magi/base.html @@ -19,7 +19,7 @@ {% endif %} {% block styles %}{% endblock %} - + {% block head %} @@ -78,7 +78,7 @@ - +