Beispiel #1
0
const setupEvents = (editor: Editor) => {
  const contentWindow = editor.getWin();
  const initialDocEle = editor.getDoc().documentElement;

  const lastWindowDimensions = Cell(Position(contentWindow.innerWidth, contentWindow.innerHeight));
  const lastDocumentDimensions = Cell(Position(initialDocEle.offsetWidth, initialDocEle.offsetHeight));

  const resize = () => {
    // Don't use the initial doc ele, as there's a small chance it may have changed
    const docEle = editor.getDoc().documentElement;

    // Check if the window or document dimensions have changed and if so then trigger a content resize event
    const outer = lastWindowDimensions.get();
    const inner = lastDocumentDimensions.get();
    if (outer.left() !== contentWindow.innerWidth || outer.top() !== contentWindow.innerHeight) {
      lastWindowDimensions.set(Position(contentWindow.innerWidth, contentWindow.innerHeight));
      Events.fireResizeContent(editor);
    } else if (inner.left() !== docEle.offsetWidth || inner.top() !== docEle.offsetHeight) {
      lastDocumentDimensions.set(Position(docEle.offsetWidth, docEle.offsetHeight));
      Events.fireResizeContent(editor);
    }
  };

  DOM.bind(contentWindow, 'resize', resize);

  // Bind to async load events and trigger a content resize event if the size has changed
  const elementLoad = DomEvent.capture(Element.fromDom(editor.getBody()), 'load', resize);

  editor.on('remove', () => {
    elementLoad.unbind();
    DOM.unbind(contentWindow, 'resize', resize);
  });
};
PluginManager.add('contextmenu', function (editor) {
  const menu = Cell(null), visibleState = Cell(false);

  Bind.setup(editor, visibleState, menu);

  return Api.get(visibleState);
});
Beispiel #3
0
PluginManager.add('fullpage', function (editor) {
  const headState = Cell(''), footState = Cell('');

  Commands.register(editor, headState);
  Buttons.register(editor);
  FilterContent.setup(editor, headState, footState);
});
Beispiel #4
0
export const SelectionTargets = (editor: Editor, selections: Selections) => {
  const targets = Cell<Option<Targets>>(Option.none());
  const changeHandlers = Cell([]);

  const findTargets = (): Option<Targets> => {
    return TableSelection.getSelectionStartCellOrCaption(editor).bind((cellOrCaption) => {
      const table = TableLookup.table(cellOrCaption);
      return table.map((table) => {
        if (Node.name(cellOrCaption) === 'caption') {
          return TableTargets.notCell(cellOrCaption);
        } else {
          return TableTargets.forMenu(selections, table, cellOrCaption);
        }
      });
    });
  };

  const resetTargets = () => {
    // Reset the targets
    targets.set(Thunk.cached(findTargets)());

    // Trigger change handlers
    Arr.each(changeHandlers.get(), (handler) => handler());
  };

  const onSetup = (api, isDisabled: (targets: Targets) => boolean) => {
    const handler = () => targets.get().fold(() => {
      api.setDisabled(true);
    }, (targets) => {
      api.setDisabled(isDisabled(targets));
    });

    // Execute the handler to set the initial state
    handler();

    // Register the handler so we can update the state when resetting targets
    changeHandlers.set(changeHandlers.get().concat([handler]));

    return () => {
      changeHandlers.set(Arr.filter(changeHandlers.get(), (h) => h !== handler));
    };
  };

  const onSetupTable = (api) => onSetup(api, (_) => false);
  const onSetupCellOrRow = (api) => onSetup(api, (targets) => Node.name(targets.element()) === 'caption');
  const onSetupMergeable = (api) => onSetup(api, (targets) => targets.mergable().isNone());
  const onSetupUnmergeable = (api) => onSetup(api, (targets) => targets.unmergable().isNone());

  editor.on('NodeChange', resetTargets);

  return {
    onSetupTable,
    onSetupCellOrRow,
    onSetupMergeable,
    onSetupUnmergeable,
    resetTargets,
    targets: () => targets.get()
  };
};
Beispiel #5
0
PluginManager.add('imagetools', function (editor) {
  const imageUploadTimerState = Cell(0);
  const lastSelectedImageState = Cell(null);

  Commands.register(editor, imageUploadTimerState);
  Buttons.register(editor);
  ContextToolbar.register(editor);

  UploadSelectedImage.setup(editor, imageUploadTimerState, lastSelectedImageState);
});
Beispiel #6
0
const register = (editor: Editor) => {
  registerCommands(editor);
  const lastForeColor = Cell(null);
  const lastBackColor = Cell(null);
  registerTextColorButton(editor, 'forecolor', 'forecolor', 'Text color', lastForeColor);
  registerTextColorButton(editor, 'backcolor', 'hilitecolor', 'Background color', lastBackColor);

  registerTextColorMenuItem(editor, 'forecolor', 'forecolor', 'Text color');
  registerTextColorMenuItem(editor, 'backcolor', 'hilitecolor', 'Background color');
};
Beispiel #7
0
export default function () {
  const store = TestHelpers.TestStore();

  const editorState = {
    start: Cell(null),
    content: Cell('')
  };

  const sPrepareState = function (node, content) {
    return Step.sync(function () {
      editorState.start.set(node);
      editorState.content.set(content);
    });
  };

  const editor = {
    selection: {
      getStart: editorState.start.get,
      getContent: editorState.content.get,
      select: Fun.noop
    },

    insertContent (data) {
      store.adder({ method: 'insertContent', data })();
    },
    execCommand (name, ui, args) {
      store.adder({ method: 'execCommand', data: Objects.wrap(name, args) })();
    },
    dom: {
      createHTML (tag, attributes, innerText) {
        return { tag, attributes, innerText };
      },
      encode: Fun.identity
    },
    focus: Fun.noop,
    ui: {
      registry: {
        getAll: () => {
          return {
            icons: {}
          };
        }
      }
    }
  };

  return {
    editor: Fun.constant(editor),
    adder: store.adder,
    assertEq: store.assertEq,
    sAssertEq: store.sAssertEq,
    sClear: store.sClear,
    sPrepareState
  };
}
Beispiel #8
0
export const renderCustomEditor = (spec: CustomEditorFoo): SimpleSpec => {
  const editorApi = Cell(Option.none());

  const memReplaced = Memento.record({
    dom: {
      tag: spec.tag
    }
  });

  const initialValue = Cell(Option.none());

  return {
    dom: {
      tag: 'div',
      classes: [ 'tox-custom-editor' ]
    },
    behaviours: Behaviour.derive([
      AddEventsBehaviour.config('editor-foo-events', [
        AlloyEvents.runOnAttached((component) => {
          memReplaced.getOpt(component).each((ta) => {
            spec.init(ta.element().dom()).then((ea) => {
              initialValue.get().each((cvalue) => {
                ea.setValue(cvalue);
              });

              initialValue.set(Option.none());
              editorApi.set(Option.some(ea));
            });
          });
        })
      ]),
      Representing.config({
        store: {
          mode: 'manual',
          getValue: () => editorApi.get().fold(
            () => initialValue.get().getOr(''),
            (ed) => ed.getValue()
          ),
          setValue: (component, value) => {
            editorApi.get().fold(
              () => {
                initialValue.set(Option.some(value));
              },
              (ed) => ed.setValue(value)
            );
          }
        }
      }),

      ComposingConfigs.self()
    ]),
    components: [memReplaced.asSpec()]
  };
};
Beispiel #9
0
PluginManager.add('spellchecker', function (editor, pluginUrl) {
  if (DetectProPlugin.hasProPlugin(editor) === false) {
    const startedState = Cell(false);
    const currentLanguageState = Cell(Settings.getLanguage(editor));
    const textMatcherState = Cell(null);
    const lastSuggestionsState = Cell({});

    Buttons.register(editor, pluginUrl, startedState, textMatcherState, currentLanguageState, lastSuggestionsState);
    SuggestionsMenu.setup(editor, pluginUrl, lastSuggestionsState, startedState, textMatcherState, currentLanguageState);
    Commands.register(editor, pluginUrl, startedState, textMatcherState, lastSuggestionsState, currentLanguageState);

    return Api.get(editor, startedState, lastSuggestionsState, textMatcherState, pluginUrl);
  }
});
Beispiel #10
0
const getDynamicSource = (isSandbox): IFrameSourcing => {
  const cachedValue = Cell('');
  return {
    getValue: (frameComponent: AlloyComponent): string => {
      // Ideally we should fetch data from the iframe...innerHtml, this triggers Corrs errors
      return cachedValue.get();
    },
    setValue: (frameComponent: AlloyComponent, html: string) => {

      if (!isSandbox) {
        Attr.set(frameComponent.element(), 'src', 'javascript:\'\'');
        // IE 6-11 doesn't support data uris on iframeComponents
        // and Edge only supports upto ~4000 chars in data uris
        // so I guess they will have to be less secure since we can't sandbox on those
        // TODO: Use sandbox if future versions of IE/Edge supports iframeComponents with data: uris.
        const doc = frameComponent.element().dom().contentWindow.document;

        doc.open();
        doc.write(html);
        doc.close();

      } else {
        Attr.set(frameComponent.element(), 'src', 'data:text/html;charset=utf-8,' + encodeURIComponent(html));
      }
      cachedValue.set(html);
    }
  };
};