| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- /* global CodeMirror dirtyReporter initLint beautify showKeyMapHelp */
- /* global showToggleStyleHelp goBackToManage updateLintReportIfEnabled */
- /* global hotkeyRerouter setupAutocomplete */
- /* global editors linterConfig updateLinter regExpTester mozParser */
- /* global makeLink createAppliesToLineWidget messageBox */
- 'use strict';
- function createSourceEditor(style) {
- // a flag for isTouched()
- let hadBeenSaved = false;
- document.documentElement.classList.add('usercss');
- $('#sections').textContent = '';
- $('#name').disabled = true;
- $('#mozilla-format-heading').parentNode.remove();
- $('#sections').appendChild(
- $element({className: 'single-editor'})
- );
- $('#header').appendChild($element({
- id: 'footer',
- appendChild: makeLink('https://github.com/openstyles/stylus/wiki/Usercss', t('externalUsercssDocument'))
- }));
- const dirty = dirtyReporter();
- dirty.onChange(() => {
- const DIRTY = dirty.isDirty();
- document.body.classList.toggle('dirty', DIRTY);
- $('#save-button').disabled = !DIRTY;
- updateTitle();
- });
- // normalize style
- if (!style.id) {
- setupNewStyle(style);
- } else {
- // style might be an object reference to background page
- style = deepCopy(style);
- }
- const cm = CodeMirror($('.single-editor'));
- editors.push(cm);
- updateMeta().then(() => {
- initLint();
- initLinterSwitch();
- cm.setValue(style.sourceCode);
- cm.clearHistory();
- cm.markClean();
- initHooks();
- initAppliesToLineWidget();
- // focus must be the last action, otherwise the style is duplicated on saving
- cm.focus();
- });
- function initAppliesToLineWidget() {
- const PREF_NAME = 'editor.appliesToLineWidget';
- const widget = createAppliesToLineWidget(cm);
- const optionEl = buildOption();
- $('#options').insertBefore(optionEl, $('#options > .option.aligned'));
- widget.toggle(prefs.get(PREF_NAME));
- prefs.subscribe([PREF_NAME], (key, value) => {
- widget.toggle(value);
- optionEl.checked = value;
- });
- optionEl.addEventListener('change', e => {
- prefs.set(PREF_NAME, e.target.checked);
- });
- function buildOption() {
- return $element({className: 'option', appendChild: [
- $element({
- tag: 'input',
- type: 'checkbox',
- id: PREF_NAME,
- checked: prefs.get(PREF_NAME)
- }),
- $element({
- tag: 'label',
- htmlFor: PREF_NAME,
- textContent: ' ' + t('appliesLineWidgetLabel'),
- title: t('appliesLineWidgetWarning')
- })
- ]});
- }
- }
- function initLinterSwitch() {
- const linterEl = $('#editor.linter');
- cm.on('optionChange', (cm, option) => {
- if (option !== 'mode') {
- return;
- }
- updateLinter();
- update();
- });
- linterEl.addEventListener('change', update);
- update();
- function update() {
- linterEl.value = linterConfig.getDefault();
- const cssLintOption = linterEl.querySelector('[value="csslint"]');
- if (cm.getOption('mode') !== 'css') {
- cssLintOption.disabled = true;
- cssLintOption.title = t('linterCSSLintIncompatible', cm.getOption('mode'));
- } else {
- cssLintOption.disabled = false;
- cssLintOption.title = '';
- }
- }
- }
- function setupNewStyle(style) {
- style.sections[0].code = ' '.repeat(prefs.get('editor.tabSize')) + '/* Insert code here... */';
- let section = mozParser.format(style);
- if (!section.includes('@-moz-document')) {
- style.sections[0].domains = ['example.com'];
- section = mozParser.format(style);
- }
- const sourceCode = `/* ==UserStyle==
- @name New Style - ${Date.now()}
- @namespace github.com/openstyles/stylus
- @version 0.1.0
- @description A new userstyle
- @author Me
- ==/UserStyle== */
- ${section}
- `;
- dirty.modify('source', '', sourceCode);
- style.sourceCode = sourceCode;
- }
- function initHooks() {
- // sidebar commands
- $('#save-button').onclick = save;
- $('#beautify').onclick = beautify;
- $('#keyMap-help').onclick = showKeyMapHelp;
- $('#toggle-style-help').onclick = showToggleStyleHelp;
- $('#cancel-button').onclick = goBackToManage;
- // enable
- $('#enabled').onchange = e => {
- const value = e.target.checked;
- dirty.modify('enabled', style.enabled, value);
- style.enabled = value;
- };
- // source
- cm.on('change', () => {
- const value = cm.getValue();
- dirty.modify('source', style.sourceCode, value);
- style.sourceCode = value;
- updateLintReportIfEnabled(cm);
- });
- // hotkeyRerouter
- cm.on('focus', () => {
- hotkeyRerouter.setState(false);
- });
- cm.on('blur', () => {
- hotkeyRerouter.setState(true);
- });
- // autocomplete
- if (prefs.get('editor.autocompleteOnTyping')) {
- setupAutocomplete(cm);
- }
- }
- function updateMeta() {
- $('#name').value = style.name;
- $('#enabled').checked = style.enabled;
- $('#url').href = style.url;
- const {usercssData: {preprocessor} = {}} = style;
- // beautify only works with regular CSS
- $('#beautify').disabled = cm.getOption('mode') !== 'css';
- updateTitle();
- return cm.setPreprocessor(preprocessor);
- }
- function updateTitle() {
- // title depends on dirty and style meta
- if (!style.id) {
- document.title = t('addStyleTitle');
- } else {
- document.title = (dirty.isDirty() ? '* ' : '') + t('editStyleTitle', [style.name]);
- }
- }
- function replaceStyle(newStyle) {
- if (!style.id && newStyle.id) {
- history.replaceState({}, '', `?id=${newStyle.id}`);
- }
- style = deepCopy(newStyle);
- updateMeta();
- if (style.sourceCode !== cm.getValue()) {
- const cursor = cm.getCursor();
- cm.setValue(style.sourceCode);
- cm.setCursor(cursor);
- }
- dirty.clear();
- hadBeenSaved = false;
- }
- function setStyleDirty(newStyle) {
- dirty.clear();
- dirty.modify('source', newStyle.sourceCode, style.sourceCode);
- dirty.modify('enabled', newStyle.enabled, style.enabled);
- }
- function toggleStyle() {
- const value = !style.enabled;
- dirty.modify('enabled', style.enabled, value);
- style.enabled = value;
- updateMeta();
- // save when toggle enable state?
- save();
- }
- function save() {
- if (!dirty.isDirty()) {
- return;
- }
- return onBackgroundReady()
- .then(() => BG.usercssHelper.save({
- reason: 'editSave',
- id: style.id,
- enabled: style.enabled,
- sourceCode: style.sourceCode
- }))
- .then(replaceStyle)
- .then(() => {
- hadBeenSaved = true;
- })
- .catch(err => {
- const contents = [String(err)];
- if (Number.isInteger(err.index)) {
- const pos = cm.posFromIndex(err.index);
- contents[0] += ` (line ${pos.line + 1} col ${pos.ch + 1})`;
- contents.push($element({
- tag: 'pre',
- textContent: drawLinePointer(pos)
- }));
- }
- console.error(err);
- messageBox.alert(contents);
- });
- function drawLinePointer(pos) {
- const SIZE = 60;
- const line = cm.getLine(pos.line);
- const pointer = ' '.repeat(pos.ch) + '^';
- const start = Math.max(Math.min(pos.ch - SIZE / 2, line.length - SIZE), 0);
- const end = Math.min(Math.max(pos.ch + SIZE / 2, SIZE), line.length);
- const leftPad = start !== 0 ? '...' : '';
- const rightPad = end !== line.length ? '...' : '';
- return leftPad + line.slice(start, end) + rightPad + '\n' +
- ' '.repeat(leftPad.length) + pointer.slice(start, end);
- }
- }
- function isTouched() {
- // indicate that the editor had been touched by the user
- return dirty.isDirty() || hadBeenSaved;
- }
- function replaceMeta(newStyle) {
- style.enabled = newStyle.enabled;
- dirty.clear('enabled');
- updateMeta();
- }
- return {
- replaceStyle,
- replaceMeta,
- setStyleDirty,
- save,
- toggleStyle,
- isDirty: dirty.isDirty,
- getStyle: () => style,
- isTouched
- };
- }
|