From b1abcdd41d4399e2e879d74e02f83b83259357bc Mon Sep 17 00:00:00 2001 From: itsKaspar Date: Wed, 22 Jun 2022 17:52:20 +0200 Subject: [PATCH] Added javascript render support by adding ' + } + + let code = base + jsopen + this.code + jsclose + + if (this._proxy) { code = prependProxyURL(code, this._proxy) } + + content.write(code) + } + if (this._titl) document.title = content.title + content.close() + this._checkForCORSerr() + this.emit('render-update') + } + + _delayUpdate (cm) { + // TODO: i feel like this could have better debounce logic, + // maybe doesn't run unless there's been an _adly worth of stillness + // (ie. nothing has been typed) in the editor? + clearTimeout(this._autoCallback) + this.emit('code-update', this.code) + this._autoCallback = setTimeout(() => { this._update(cm) }, this._adly) + this._prevState = this.cm.getValue() + } + + async _update (cm) { + const h = document.querySelector('.CodeMirror-hints') + if (this._hint && this._shouldHint(cm) && !h) cm.showHint() + this.errz = (this._lint && !h) ? await linter(cm) : [] + this.errz = this.errz.length > 0 ? this._rmvExceptions(this.errz) : this.errz + if (this.errz) this.emit('lint-error', this.errz) + if (this._auto && !h && this._passThroughErrz(this.errz)) this.update() + } + + // ~ ~ ~ errz ~ ~ ~ + + _passThroughErrz (errz) { + if (this._rerr) return true + else return errz.length === 0 + } + + _checkForCORSerr () { + const emitErr = (path, err) => { + const lines = this.cm.getValue().split('\n') + const filtered = lines.filter(s => s.includes(path)) + filtered.forEach(str => { + const arr = str.split('/') + const img = arr[arr.length - 1].replace(/"/g, '').replace(/>/g, '') + if (!this.errz) this.errz = [] + this.errz.push({ + type: 'error', + language: 'html', + message: err.toString(), + friendly: `The ${img} image you are trying to request is on a server that prevents "cross domain requests"`, + line: lines.indexOf(str) + 1, + col: 0 + }) + }) + this.emit('lint-error', this.errz) + } + // find all img elements, check for CORS errors + // ex: https://www.iconsdb.com/icons/preview/white/dvd-xxl.png + const iframe = this.render.querySelector('iframe') + let doc = iframe.contentWindow || iframe.contentDocument + if (doc.document) doc = doc.document + const paths = [...doc.querySelectorAll('img')] + paths.map(ele => ele.getAttribute('src')).forEach(p => { + window.fetch(p).then(r => { /* ok */ }).catch(e => emitErr(p, e)) + }) + } + + _err2str (err, specific) { + const obj = {} + if (err.language === 'javascript') { + obj.rule = err.jshint.code + if (specific) obj.message = err.message + } else if (err.language === 'css') { + obj.rule = err.rule + if (specific) obj.message = err.message + } else { // html + obj.rule = err.rule.id + if (specific) obj.message = err.message + } + return JSON.stringify(obj) + } + + _rmvExceptions (errz) { + // check for specific error exceptions + if (this._errExceptions.length > 0) { + for (let i = errz.length - 1; i >= 0; i--) { + const s1 = this._errExceptions.includes(this._err2str(errz[i])) + const s2 = this._errExceptions.includes(this._err2str(errz[i], true)) + if (s1 || s2) errz.splice(i, 1) + } + } + // then check for custom elements + const eles = Object.keys(this._customElements) + if (eles.length > 0) { + for (let i = errz.length - 1; i >= 0; i--) { + if (errz[i].rule && errz[i].rule.id === 'standard-elements') { + if (eles.includes(errz[i].evidence)) errz.splice(i, 1) + } + } + } + return errz + } + + // ~ ~ ~ hinting ~ ~ ~ + + _shouldHint (cm) { + const pos = cm.getCursor() + const tok = cm.getTokenAt(pos) + const line = cm.getLine(pos.line) + // check to make sure user is actually typing something + const typing = tok.string.replace(/\s/g, '').length > 0 + const nextChar = line.slice(tok.end, tok.end + 1) + // check to make sure the cursor is at the end of a lone word + // otherwise we'll be creating hint menus all the time + const alone = nextChar === '' || nextChar === ' ' + // allow hinting when inside parens + const paren = nextChar === ')' + // check to see if the cursor is inside of a tag (for attributes) + const tagAttr = nextChar === '>' + // check for JS event args (rest of logic in jsHinter) + const jsArg = cm.getModeAt(pos).name === 'javascript' && nextChar === ',' + + return typing && (alone || paren || tagAttr || jsArg) + } + + _placeHintCursor (cm, data) { + const swap = (marker) => { + const arr = this.code.split('\n') + const str = arr.find(s => s.includes(marker)) + const idx = arr.indexOf(str) + const col = str.indexOf(marker) + this.code = this.code.replace(marker, '') + return { line: idx, ch: col } + } + + const cur = '' + const curStart = '' + const curEnd = '' + if (typeof data === 'string') data = { text: data } + if (data.text.includes(cur) && this.code.includes(cur)) { + const pos = swap(cur) + cm.setCursor(pos) + } else if (data.text.includes(curStart) && this.code.includes(curStart)) { + const start = swap(curStart) + const end = swap(curEnd) + cm.setSelection(start, end) + } else if (data.text.includes('> { this._delayUpdate(cm) }) + CodeMirror.on(res, 'pick', (d) => { this._placeHintCursor(cm, d) }) + CodeMirror.on(res, 'select', (data) => { + const language = lan === 'xml' ? 'html' : lan + this.emit('hint-select', { language, data }) + }) + return res + } + + // ~ ~ ~ misc ~ ~ ~ + + _customElementsNfo (obj) { // ...for edu-info + if (obj.language === 'html' && obj.type === 'element' && !obj.nfo) { + if (this._customElements[obj.data]) { + obj.nfo = this._customElements[obj.data] + } + } + return obj + } + + _customAttributesNfo (obj) { + if (obj.language === 'html' && obj.type === 'attribute') { + const pos = this.cm.getCursor() + const tok = this.cm.getTokenAt(pos) + const tag = tok.state.htmlState.tagName + const ele = this._customElements[tag] + const nfo = this._customAttributes[obj.data] + if (ele && nfo) obj.nfo = nfo + } + return obj + } + + _tidy (checkOnly) { + const o = { + indent_size: 2, + indent_inner_html: true, + extra_liners: [] + } + + const clean = (this._lang === 'css') + ? beautifyCSS(this.code, o) : (this._lang === 'javascript') + ? beautifyJS(this.code, o) : beautifyHTML(this.code, o) + + if (!checkOnly) this.code = clean + return this.code === clean + } + + _updateTheme (v) { + if (!this.themes[v]) return this.err(`${v} is not a valid theme`) + + this._clrz = v + for (const p in this.themes[v]) { + if (p !== 'metadata') { + const cssVar = `--netizen-${p.replace(/_/g, '-')}` + const val = this.themes[v][p] + document.documentElement.style.setProperty(cssVar, val) + } + } + } + + _updateBG (v) { + if (typeof v !== 'boolean') return this.err('background must be a boolean') + + this._bgcl = v + const bg = this.themes[this.theme].background + const val = (this._bgcl) ? bg : bg + '00' + document.documentElement.style.setProperty('--netizen-background', val) + } + + _compareTwoStrings (a, b) { return stringSimilarity.compareTwoStrings(a, b) } + _findBestMatch (a, b) { return stringSimilarity.findBestMatch(a, b) } + + _decode (code) { return pako.inflate(window.atob(code), { to: 'string' }) } + _encode (code) { return window.btoa(pako.deflate(code, { to: 'string' })) } + + prependProxyURL (code, proxy) { return prependProxyURL(code, proxy) } +} + +window.Netitor = Netitor -},{}],99:[function(require,module,exports){ -module.exports={ - "@charset": { - "url": "https://developer.mozilla.org//en-US/docs/Web/CSS/@charset", - "status": "standard", - "keyword": { - "html": "@charset", - "text": "@charset" - }, - "description": { - "html": "The @charset CSS at-rule specifies the character encoding used in the style sheet. It must be the first element in the style sheet and not be preceded by any character; as it is not a nested statement, it cannot be used inside conditional group at-rules. If several @charset at-rules are defined, only the first one is used, and it cannot be used inside a style attribute on an HTML element or inside the \"style\" element where the character set of the HTML page is relevant.", - "text": "The @charset CSS at-rule specifies the character encoding used in the style sheet. It must be the first element in the style sheet and not be preceded by any character; as it is not a nested statement, it cannot be used inside conditional group at-rules. If several @charset at-rules are defined, only the first one is used, and it cannot be used inside a style attribute on an HTML element or inside the