Example #1
0
const toRect = function (rect) {
  return {
    left: Fun.constant(rect.left),
    top: Fun.constant(rect.top),
    right: Fun.constant(rect.right),
    bottom: Fun.constant(rect.bottom),
    width: Fun.constant(rect.width),
    height: Fun.constant(rect.height)
  };
};
Example #2
0
  return Future.nu(function (callback) {
    const getCurrent = Fun.curry(getScrollTop, element);

    const update = function (newScroll) {
      element.dom().scrollTop = newScroll;
      Css.set(element, 'top', (getTop(element) + ANIMATION_STEP) + 'px');
    };

    const finish = function (/* dest */) {
      element.dom().scrollTop = destination;
      Css.set(element, 'top', finalTop + 'px');
      callback(destination);
    };

    animator.animate(getCurrent, destination, ANIMATION_STEP, update, finish, ANIMATION_RATE);
  });
Example #3
0
  return Future.nu(function (callback) {
    const getCurrent = Fun.curry(getTop, element);

    const update = function (newTop) {
      Css.set(element, 'top', newTop + 'px');
    };

    const finish = function (/* dest */) {
      update(destination);
      callback(destination);
    };

    const distance = Math.abs(destination - getCurrent());
    const step = Math.ceil(distance / NUM_TOP_ANIMATION_FRAMES);
    animator.animate(getCurrent, destination, step, update, finish, ANIMATION_RATE);
  });
Example #4
0
const insertSpaceOrNbspAtSelection = (editor: Editor): boolean => {
  const pos = CaretPosition.fromRangeStart(editor.selection.getRng());
  const root = Element.fromDom(editor.getBody());

  if (editor.selection.isCollapsed()) {
    const isInlineTarget = Fun.curry(InlineUtils.isInlineTarget, editor);
    const caretPosition = CaretPosition.fromRangeStart(editor.selection.getRng());

    return BoundaryLocation.readLocation(isInlineTarget, editor.getBody(), caretPosition)
      .bind(locationToCaretPosition(root))
      .bind(insertInlineBoundarySpaceOrNbsp(root, pos))
      .exists(setSelection(editor));
  } else {
    return false;
  }
};
  const sTestScenario = function (rawScenario) {
    const scenario = ValueSchema.asRawOrDie('Checking scenario', ValueSchema.objOf([
      FieldSchema.strict('label'),
      FieldSchema.defaulted('content', ''),
      FieldSchema.defaulted('node', Element.fromText('')),
      FieldSchema.strictObjOf('fields', [
        FieldSchema.option('url'),
        FieldSchema.option('text'),
        FieldSchema.option('title'),
        FieldSchema.option('target')
      ]),
      FieldSchema.strict('expected'),
      FieldSchema.defaulted('beforeExecute', Step.pass),
      FieldSchema.defaulted('mutations', Fun.constant(Step.pass))
    ]), rawScenario);

    return Logger.t(
      scenario.label,
      GeneralSteps.sequence([
        tEditor.sPrepareState(scenario.node.dom(), scenario.content),
        sClickLink,
        TestUi.sSetFieldOptValue(scenario.fields.url),
        sClickNext,
        sAssertTextFocused,
        TestUi.sSetFieldOptValue(scenario.fields.text),
        sClickNext,
        sAssertTitleFocused,
        TestUi.sSetFieldOptValue(scenario.fields.title),
        sClickNext,
        sAssertTargetFocused,
        TestUi.sSetFieldOptValue(scenario.fields.target),
        sClickPrev,
        sAssertTitleFocused,
        sClickPrev,
        sAssertTextFocused,
        sClickPrev,
        sAssertUrlFocused,
        scenario.beforeExecute,
        Keyboard.sKeydown(doc, Keys.enter(), { }),
        tEditor.sAssertEq('Checking insert content', scenario.expected),
        scenario.mutations(scenario.node),
        tEditor.sClear

      ])
    );
  };
Example #6
0
const setContent = (editor: Editor, content: Content, args: SetContentArgs = {}): Content => {
  args.format = args.format ? args.format : defaultFormat;
  args.set = true;
  args.content = isTreeNode(content) ? '' : content;

  if (!isTreeNode(content) && !args.no_events) {
    editor.fire('BeforeSetContent', args);
    content = args.content;
  }

  return Option.from(editor.getBody())
    .fold(
      Fun.constant(content),
      (body) => isTreeNode(content) ? setContentTree(editor, body, content, args) : setContentString(editor, body, content, args)
    );

};
const findLocationTraverse = function (forward, isInlineTarget, rootNode, fromLocation, pos) {
  const from = InlineUtils.normalizePosition(forward, pos);
  const to = CaretFinder.fromPosition(forward, rootNode, from).map(Fun.curry(InlineUtils.normalizePosition, forward));

  const location = to.fold(
    function () {
      return fromLocation.map(outside);
    },
    function (to) {
      return readLocation(isInlineTarget, rootNode, to)
        .map(Fun.curry(betweenInlines, forward, isInlineTarget, rootNode, from, to))
        .filter(Fun.curry(skipNoMovement, fromLocation));
    }
  );

  return location.filter(isValidLocation);
};
Example #8
0
const deriveReplacing = (spec, component: AlloyComponent) => {
  if (component.hasConfigured(Representing)) {
    /* TODO type this

    [{
      dom: {
        tag: 'div',
        classes: [ 'my-class' ],
        innerHtml: text
      }
    } ... ];
    */
    return {
      updateButton: Fun.curry(Replacing.set, component)
    };
  }
};
export default function () {
  const store = 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
  };

  return {
    editor: Fun.constant(editor),
    adder: store.adder,
    assertEq: store.assertEq,
    sAssertEq: store.sAssertEq,
    sClear: store.sClear,
    sPrepareState
  };
}
Example #10
0
      return getWinFromFrame(frame).map(function (win) {

        const html = Element.fromDom(doc.dom().documentElement);

        const getCursorBox = editor.getCursorBox.getOrThunk(function () {
          return function () {
            return WindowSelection.get(win).bind(function (sel) {
              return WindowSelection.getFirstRect(win, sel).orThunk(function () {
                return tryFallbackBox(win);
              });
            });
          };
        });

        const setSelection = editor.setSelection.getOrThunk(function () {
          return function (start, soffset, finish, foffset) {
            WindowSelection.setExact(win, start, soffset, finish, foffset);
          };
        });

        const clearSelection = editor.clearSelection.getOrThunk(function () {
          return function () {
            WindowSelection.clear(win);
          };
        });

        return {
          body: Fun.constant(body),
          doc: Fun.constant(doc),
          win: Fun.constant(win),
          html: Fun.constant(html),
          getSelection: Fun.curry(getSelectionFromFrame, frame),
          setSelection,
          clearSelection,
          frame: Fun.constant(frame),

          onKeyup: getOrListen(editor, doc, 'onKeyup', 'keyup'),
          onNodeChanged: getOrListen(editor, doc, 'onNodeChanged', 'selectionchange'),
          onDomChanged: editor.onDomChanged, // consider defaulting with MutationObserver

          onScrollToCursor: editor.onScrollToCursor,
          onScrollToElement: editor.onScrollToElement,
          onToReading: editor.onToReading,
          onToEditing: editor.onToEditing,

          onToolbarScrollStart: editor.onToolbarScrollStart,
          onTouchContent: editor.onTouchContent,
          onTapContent: editor.onTapContent,
          onTouchToolstrip: editor.onTouchToolstrip,

          getCursorBox
        };
      });
Example #11
0
const parseItem: Parser = (depth: number, itemSelection: Option<ItemTuple>, selectionState: Cell<boolean>, item: Element): Entry[] => {
  const curriedParseList = Fun.curry(parseList, depth, itemSelection, selectionState);

  const updateSelectionState = (itemRange: ItemRange) => itemSelection.each((selection) => {
    if (Compare.eq(itemRange === ItemRange.Start ? selection.start : selection.end, item)) {
      selectionState.set(itemRange === ItemRange.Start);
    }
  });

  return Traverse.firstChild(item).filter(isList).fold(() => {
    updateSelectionState(ItemRange.Start);
    const fromCurrentItem: Entry = createEntry(item, depth, selectionState.get());
    updateSelectionState(ItemRange.End);
    const fromChildList: Entry[] = Traverse.lastChild(item).filter(isList).map(curriedParseList).getOr([]);

    return [ fromCurrentItem, ...fromChildList ];
  }, curriedParseList);
};
Example #12
0
const register = function (editor: Editor, clipboard: Clipboard) {
  const postRender = Fun.curry(stateChange, editor, clipboard);

  editor.addButton('pastetext', {
    active: false,
    icon: 'pastetext',
    tooltip: 'Paste as text',
    cmd: 'mceTogglePlainTextPaste',
    onPostRender: postRender
  });

  editor.addMenuItem('pastetext', {
    text: 'Paste as text',
    selectable: true,
    active: clipboard.pasteFormat,
    cmd: 'mceTogglePlainTextPaste',
    onPostRender: postRender
  });
};
const deleteElement = function (editor, forward, elm) {
  const afterDeletePos = findCaretPosOutsideElmAfterDelete(forward, editor.getBody(), elm.dom());
  const parentBlock = PredicateFind.ancestor(elm, Fun.curry(isBlock, editor), eqRawNode(editor.getBody()));
  const normalizedAfterDeletePos = deleteNormalized(elm, afterDeletePos);

  if (editor.dom.isEmpty(editor.getBody())) {
    editor.setContent('');
    editor.selection.setCursorLocation();
  } else {
    parentBlock.bind(paddEmptyBlock).fold(
      function () {
        setSelection(editor, forward, normalizedAfterDeletePos);
      },
      function (paddPos) {
        setSelection(editor, forward, Option.some(paddPos));
      }
    );
  }
};
Example #14
0
      .each((cellOrCaption) => {
        const tableOpt = TableLookup.table(cellOrCaption, isRoot);
        tableOpt.filter(Fun.not(isRoot)).each((table) => {
          const cursor = Element.fromText('');
          Insert.after(table, cursor);
          Remove.remove(table);

          if (editor.dom.isEmpty(editor.getBody())) {
            editor.setContent('');
            editor.selection.setCursorLocation();
          } else {
            const rng = editor.dom.createRng();
            rng.setStart(cursor.dom(), 0);
            rng.setEnd(cursor.dom(), 0);
            editor.selection.setRng(rng);
            editor.nodeChanged();
          }
        });
      });
Example #15
0
const renderIframeBody = (spec: Types.UrlDialog.UrlDialog) => {
  const bodySpec = {
    dom: {
      tag: 'div',
      classes: [ 'tox-dialog__content-js' ]
    },
    components: [
      {
        dom: {
          tag: 'div',
          classes: [ 'tox-dialog__body-iframe' ]
        },
        components: [
          NavigableObject.craft({
            dom: {
              tag: 'iframe',
              attributes: {
                src: spec.url
              }
            },
            behaviours: Behaviour.derive([
              Tabstopping.config({ }),
              Focusing.config({ })
            ])
          })
        ]
      }
    ],
    behaviours: Behaviour.derive([
      Keying.config({
        mode: 'acyclic',
        useTabstopAt: Fun.not(NavigableObject.isPseudoStop)
      })
    ])
  };

  return ModalDialog.parts().body(
    bodySpec
  );
};
Example #16
0
  const toBlobInfo = function (o: BlobInfoData): BlobInfo {
    let id, name;

    if (!o.blob || !o.base64) {
      throw new Error('blob and base64 representations of the image are required for BlobInfo to be created');
    }

    id = o.id || Uuid.uuid('blobid');
    name = o.name || id;

    return {
      id: Fun.constant(id),
      name: Fun.constant(name),
      filename: Fun.constant(name + '.' + mimeToExt(o.blob.type)),
      blob: Fun.constant(o.blob),
      base64: Fun.constant(o.base64),
      blobUri: Fun.constant(o.blobUri || URL.createObjectURL(o.blob)),
      uri: Fun.constant(o.uri)
    };
  };
Example #17
0
ThemeManager.add('silver', (editor): Theme => {
  const { mothership, uiMothership, backstage, renderUI, getUi }: RenderInfo = Render.setup(editor);

  FormatControls.setup(editor, backstage);

  Debugging.registerInspector(Id.generate('silver-demo'), mothership);
  Debugging.registerInspector(Id.generate('silver-ui-demo'), uiMothership);

  Autocompleter.register(editor, backstage.shared);

  const windowMgr = WindowManager.setup({ editor, backstage });

  return {
    renderUI,
    getWindowManagerImpl: Fun.constant(windowMgr),
    getNotificationManagerImpl: () => {
      return NotificationManagerImpl(editor, {backstage}, uiMothership);
    },
    // TODO: move to editor.ui namespace
    ui: getUi()
  };
});
Example #18
0
export default function (settings, editor: Editor) {
  let dom: DOMUtils, schema: Schema, htmlParser;
  const tempAttrs = ['data-mce-selected'];

  dom = editor && editor.dom ? editor.dom : DOMUtils.DOM;
  schema = editor && editor.schema ? editor.schema : Schema(settings);
  settings.entity_encoding = settings.entity_encoding || 'named';
  settings.remove_trailing_brs = 'remove_trailing_brs' in settings ? settings.remove_trailing_brs : true;

  htmlParser = DomParser(settings, schema);
  DomSerializerFilters.register(htmlParser, settings, dom);

  const serialize = function (node, parserArgs?) {
    const args = Merger.merge({ format: 'html' }, parserArgs ? parserArgs : {});
    const targetNode = DomSerializerPreProcess.process(editor, node, args);
    const html = getHtmlFromNode(dom, targetNode, args);
    const rootNode = parseHtml(htmlParser, html, args);
    return args.format === 'tree' ? rootNode : toHtml(editor, settings, schema, rootNode, args);
  };

  return {
    schema,
    addNodeFilter: htmlParser.addNodeFilter,
    addAttributeFilter: htmlParser.addAttributeFilter,
    serialize,
    addRules (rules) {
      schema.addValidElements(rules);
    },
    setRules (rules) {
      schema.setValidElements(rules);
    },
    addTempAttr: Fun.curry(addTempAttr, htmlParser, tempAttrs),
    getTempAttrs () {
      return tempAttrs;
    }
  };
}
Example #19
0
  return function () {
    const img = getSelectedImage(editor), originalSize = ImageSize.getNaturalImageSize(img);

    const handleDialogBlob = function (blob) {
      return new Promise(function (resolve) {
        BlobConversions.blobToImage(blob).
          then(function (newImage) {
            const newSize = ImageSize.getNaturalImageSize(newImage);

            if (originalSize.w !== newSize.w || originalSize.h !== newSize.h) {
              if (ImageSize.getImageSize(img)) {
                ImageSize.setImageSize(img, newSize);
              }
            }

            URL.revokeObjectURL(newImage.src);
            resolve(blob);
          });
      });
    };

    const openDialog = function (editor, imageResult) {
      return Dialog.edit(editor, imageResult).then(handleDialogBlob).
        then(ResultConversions.blobToImageResult).
        then(function (imageResult) {
          return updateSelectedImage(editor, imageResult, true, imageUploadTimerState);
        }, function () {
          // Close dialog
        });
    };

    findSelectedBlob(editor).
      then(ResultConversions.blobToImageResult).
      then(Fun.curry(openDialog, editor), function (error) {
        displayError(editor, error);
      });
  };
Example #20
0
          AlloyForm.sketch((parts) => {
            return {
              dom: {
                tag: 'div',
                classes: [ 'tox-form' ]
              },
              components: Arr.map(tab.items, (item) => interpretInForm(parts, item, backstage)),
              formBehaviours: Behaviour.derive([
                Keying.config({
                  mode: 'acyclic',
                  useTabstopAt: Fun.not(NavigableObject.isPseudoStop)
                }),

                AddEventsBehaviour.config('TabView.form.events', [
                  AlloyEvents.runOnAttached(setDataOnForm),
                  AlloyEvents.runOnDetached(updateDataWithForm)
                ]),
                Receiving.config({
                  channels: Objects.wrapAll([
                    {
                      key: SendDataToSectionChannel,
                      value:  {
                        onReceive: updateDataWithForm
                      }
                    },
                    {
                      key: SendDataToViewChannel,
                      value: {
                        onReceive: setDataOnForm
                      }
                    }
                  ])
                })
              ])
            };
          })
Example #21
0
const getPluginKeys = function (editor) {
  const keys = Obj.keys(editor.plugins);
  return editor.settings.forced_plugins === undefined ?
    keys :
    Arr.filter(keys, Fun.not(Fun.curry(Arr.contains, editor.settings.forced_plugins)));
};
Example #22
0
/**
 * PluginsTab.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

import { Arr, Fun, Obj, Strings } from '@ephox/katamari';
import I18n from 'tinymce/core/api/util/I18n';
import PluginUrls from '../data/PluginUrls';

const makeLink = Fun.curry(Strings.supplant, '<a href="${url}" target="_blank" rel="noopener">${name}</a>');

const maybeUrlize = function (editor, key) {
  return Arr.find(PluginUrls.urls, function (x) {
    return x.key === key;
  }).fold(function () {
    const getMetadata = editor.plugins[key].getMetadata;
    return typeof getMetadata === 'function' ? makeLink(getMetadata()) : key;
  }, function (x) {
    return makeLink({ name: x.name, url: 'https://www.tinymce.com/docs/plugins/' + x.key });
  });
};

const getPluginKeys = function (editor) {
  const keys = Obj.keys(editor.plugins);
  return editor.settings.forced_plugins === undefined ?
    keys :
UnitTest.asynctest('browser.tinymce.core.keyboard.ArrowKeysTableTest', (success, failure) => {
    const browser = PlatformDetection.detect().browser;

    ModernTheme();

    const table = (html: string) => ApproxStructure.fromHtml('<table><tbody><tr><td>' + html + '</td></tr></tbody></table>');
    const block = ApproxStructure.fromHtml('<p><br></p>');
    const caret = (type: string) => {
      return ApproxStructure.fromHtml(`<p data-mce-caret="${type}" data-mce-bogus="all"><br data-mce-bogus="1"></p>`);
    };
    const visualCaret = (before: boolean) => {
      const caretClass = before ? 'mce-visual-caret-before' : 'mce-visual-caret';
      return ApproxStructure.fromHtml(`<div class="mce-visual-caret ${caretClass}" data-mce-bogus="all"></div>`);
    };

    const caretBefore = Fun.curry(caret, 'before');
    const caretAfter = Fun.curry(caret, 'after');
    const visualCaretBefore = Fun.curry(visualCaret, true);
    const visualCaretAfter = Fun.curry(visualCaret, false);
    const buildBody = (children) => ApproxStructure.build((s, str, arr) => s.element('body', { children }));

    TinyLoader.setup(function (editor, onSuccess, onFailure) {
      Pipeline.async({}, [
        Logger.t('FakeCaret before/after table', GeneralSteps.sequence(browser.isEdge() || browser.isFirefox() ? [
          Logger.t('Move fake caret left before table', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table>'),
            ApiChains.cSetCursor([0, 0, 0, 0, 0], 0),
            ApiChains.cAssertContentStructure(buildBody([ table('1') ])),
            ActionChains.cContentKeystroke(Keys.left()),
            ApiChains.cAssertContentStructure(buildBody([ caretBefore(), table('1'), visualCaretBefore() ])),
            ApiChains.cAssertSelection([0], 0, [0], 0)
          ])),
          Logger.t('Move fake caret right after table', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table>'),
            ApiChains.cSetCursor([0, 0, 0, 0, 0], 1),
            ApiChains.cAssertContentStructure(buildBody([ table('1') ])),
            ActionChains.cContentKeystroke(Keys.right()),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), caretAfter(), visualCaretAfter() ])),
            ApiChains.cAssertSelection([1], 0, [1], 0)
          ])),
          Logger.t('Move fake caret right after table then right again before other table', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table><table><tbody><tr><td>2</td></tr></tbody></table>'),
            ApiChains.cSetCursor([0, 0, 0, 0, 0], 1),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), table('2') ])),
            ActionChains.cContentKeystroke(Keys.right()),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), caretAfter(), table('2'), visualCaretAfter() ])),
            ApiChains.cAssertSelection([1], 0, [1], 0),
            ActionChains.cContentKeystroke(Keys.right()),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), caretBefore(), table('2'), visualCaretBefore() ])),
            ApiChains.cAssertSelection([1], 0, [1], 0)
          ])),
          Logger.t('Move fake caret left before table then left again after other table', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table><table><tbody><tr><td>2</td></tr></tbody></table>'),
            ApiChains.cSetCursor([1, 0, 0, 0, 0], 0),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), table('2') ])),
            ActionChains.cContentKeystroke(Keys.left()),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), caretBefore(), table('2'), visualCaretBefore() ])),
            ApiChains.cAssertSelection([1], 0, [1], 0),
            ActionChains.cContentKeystroke(Keys.left()),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), caretAfter(), table('2'), visualCaretAfter() ])),
            ApiChains.cAssertSelection([1], 0, [1], 0)
          ])),
          Logger.t('Move fake up for when table is first element', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table>'),
            ApiChains.cSetCursor([0, 0, 0, 0, 0], 0),
            ApiChains.cAssertContentStructure(buildBody([ table('1') ])),
            ActionChains.cContentKeystroke(Keys.up()),
            ApiChains.cAssertContentStructure(buildBody([ block, table('1') ])),
            ApiChains.cAssertSelection([0], 0, [0], 0)
          ])),
          Logger.t('Move fake down for when table is last element', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table>'),
            ApiChains.cSetCursor([0, 0, 0, 0, 0], 1),
            ApiChains.cAssertContentStructure(buildBody([ table('1') ])),
            ActionChains.cContentKeystroke(Keys.down()),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), block ])),
            ApiChains.cAssertSelection([1], 0, [1], 0)
          ])),
          Logger.t('Move fake up for when table is first element but not when caret is not as start', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table>'),
            ApiChains.cSetCursor([0, 0, 0, 0, 0], 1),
            ApiChains.cAssertContentStructure(buildBody([ table('1') ])),
            ActionChains.cContentKeystroke(Keys.up()),
            ApiChains.cAssertContentStructure(buildBody([ block, table('1') ])),
            ApiChains.cAssertSelection([0], 0, [0], 0)
          ])),
          Logger.t('Move fake down for when table is last element but not when caret is not as end', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent('<table><tbody><tr><td>1</td></tr></tbody></table>'),
            ApiChains.cSetCursor([0, 0, 0, 0, 0], 0),
            ApiChains.cAssertContentStructure(buildBody([ table('1') ])),
            ActionChains.cContentKeystroke(Keys.down()),
            ApiChains.cAssertContentStructure(buildBody([ table('1'), block ])),
            ApiChains.cAssertSelection([1], 0, [1], 0)
          ]))] : []
        )),

        Logger.t('Table cell navigation', GeneralSteps.sequence([
          Logger.t('Should move to the cell above the current cell on key up', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <table>
                <tbody>
                  <tr><td>1</td><td>2</td></tr>
                  <tr><td>2</td><td>3</td></tr>
                </tbody>
              </table>
            `),
            ApiChains.cSetCursor([0, 0, 1, 1, 0], 0),
            ActionChains.cContentKeystroke(Keys.up()),
            ApiChains.cAssertSelection([0, 0, 0, 1, 0], 0, [0, 0, 0, 1, 0], 0)
          ])),
          Logger.t('Should move to the cell below the current cell on key down', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <table>
                <tbody>
                  <tr><td>1</td><td>2</td></tr>
                  <tr><td>2</td><td>3</td></tr>
                </tbody>
              </table>
            `),
            ApiChains.cSetCursor([0, 0, 0, 1, 0], 0),
            ActionChains.cContentKeystroke(Keys.down()),
            ApiChains.cAssertSelection([0, 0, 1, 1, 0], 0, [0, 0, 1, 1, 0], 0)
          ])),
          Logger.t('Should move to the content above when the caret is a first table row', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <p>a<p>
              <table>
                <tbody>
                  <tr><td>1</td><td>2</td></tr>
                  <tr><td>2</td><td>3</td></tr>
                </tbody>
              </table>
            `),
            ApiChains.cSetCursor([1, 0, 0, 1, 0], 0),
            ActionChains.cContentKeystroke(Keys.up()),
            ApiChains.cAssertSelection([0, 0], 1, [0, 0], 1)
          ])),
          Logger.t('Should move to the content below if the caret is a last table row', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <table>
                <tbody>
                  <tr><td>1</td><td>2</td></tr>
                  <tr><td>2</td><td>3</td></tr>
                </tbody>
              </table>
              <p>a<p>
            `),
            ApiChains.cSetCursor([0, 0, 1, 1, 0], 0),
            ActionChains.cContentKeystroke(Keys.down()),
            ApiChains.cAssertSelection([1, 0], 1, [1, 0], 1)
          ])),
          Logger.t('Should not move down if the caret is on first line in table cell <br>', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <table>
                <tbody>
                  <tr><td>1</td><td>2<br>3</td></tr>
                  <tr><td>4</td><td>5</td></tr>
                </tbody>
              </table>
            `),
            ApiChains.cSetCursor([0, 0, 0, 1, 0], 0),
            ActionChains.cContentKeystroke(Keys.down()),
            ApiChains.cAssertSelection([0, 0, 0, 1, 0], 0, [0, 0, 0, 1, 0], 0)
          ])),
          Logger.t('Should not move up if the caret is on last line in table cell <br>', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <table>
                <tbody>
                  <tr><td>1</td><td>2</td></tr>
                  <tr><td>3</td><td>4<br>5</td></tr>
                </tbody>
              </table>
            `),
            ApiChains.cSetCursor([0, 0, 1, 1, 2], 0),
            ActionChains.cContentKeystroke(Keys.up()),
            ApiChains.cAssertSelection([0, 0, 1, 1, 2], 0, [0, 0, 1, 1, 2], 0)
          ])),
          Logger.t('Should not move down if the caret is on first line in table cell <p>', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <table>
                <tbody>
                  <tr><td>1</td><td><p>2</p><p>3</p></td></tr>
                  <tr><td>4</td><td>5</td></tr>
                </tbody>
              </table>
            `),
            ApiChains.cSetCursor([0, 0, 0, 1, 0, 0], 0),
            ActionChains.cContentKeystroke(Keys.down()),
            ApiChains.cAssertSelection([0, 0, 0, 1, 0, 0], 0, [0, 0, 0, 1, 0, 0], 0)
          ])),
          Logger.t('Should not move up if the caret is on last line in table cell <p>', Chain.asStep(editor, [
            ApiChains.cFocus,
            ApiChains.cSetContent(`
              <table>
                <tbody>
                  <tr><td>1</td><td>2</td></tr>
                  <tr><td>3</td><td><p>4</p><p>5</p></td></tr>
                </tbody>
              </table>
            `),
            ApiChains.cSetCursor([0, 0, 1, 1, 1, 0], 0),
            ActionChains.cContentKeystroke(Keys.up()),
            ApiChains.cAssertSelection([0, 0, 1, 1, 1, 0], 0, [0, 0, 1, 1, 1, 0], 0)
          ]))
        ]))
      ], onSuccess, onFailure);
    }, {
      skin_url: '/project/js/tinymce/skins/lightgray'
    }, success, failure);
  }
Example #24
0
const hasStyle = function (dom, name) {
  return Fun.curry(function (name, node) {
    return !!(node && FormatUtils.getStyle(dom, node, name));
  }, name);
};
export default function (editor) {
  const formats = FormatRegistry(editor);
  const formatChangeState = Cell(null);

  FormatShortcuts.setup(editor);
  CaretFormat.setup(editor);

  return {
    /**
     * Returns the format by name or all formats if no name is specified.
     *
     * @method get
     * @param {String} name Optional name to retrieve by.
     * @return {Array/Object} Array/Object with all registered formats or a specific format.
     */
    get: formats.get,

    /**
     * Registers a specific format by name.
     *
     * @method register
     * @param {Object/String} name Name of the format for example "bold".
     * @param {Object/Array} format Optional format object or array of format variants
     * can only be omitted if the first arg is an object.
     */
    register: formats.register,

    /**
     * Unregister a specific format by name.
     *
     * @method unregister
     * @param {String} name Name of the format for example "bold".
     */
    unregister: formats.unregister,

    /**
     * Applies the specified format to the current selection or specified node.
     *
     * @method apply
     * @param {String} name Name of format to apply.
     * @param {Object} vars Optional list of variables to replace within format before applying it.
     * @param {Node} node Optional node to apply the format to defaults to current selection.
     */
    apply: Fun.curry(ApplyFormat.applyFormat, editor),

    /**
     * Removes the specified format from the current selection or specified node.
     *
     * @method remove
     * @param {String} name Name of format to remove.
     * @param {Object} vars Optional list of variables to replace within format before removing it.
     * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
     */
    remove: Fun.curry(RemoveFormat.remove, editor),

    /**
     * Toggles the specified format on/off.
     *
     * @method toggle
     * @param {String} name Name of format to apply/remove.
     * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
     * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
     */
    toggle: Fun.curry(ToggleFormat.toggle, editor, formats),

    /**
     * Matches the current selection or specified node against the specified format name.
     *
     * @method match
     * @param {String} name Name of format to match.
     * @param {Object} vars Optional list of variables to replace before checking it.
     * @param {Node} node Optional node to check.
     * @return {boolean} true/false if the specified selection/node matches the format.
     */
    match: Fun.curry(MatchFormat.match, editor),

    /**
     * Matches the current selection against the array of formats and returns a new array with matching formats.
     *
     * @method matchAll
     * @param {Array} names Name of format to match.
     * @param {Object} vars Optional list of variables to replace before checking it.
     * @return {Array} Array with matched formats.
     */
    matchAll: Fun.curry(MatchFormat.matchAll, editor),

    /**
     * Return true/false if the specified node has the specified format.
     *
     * @method matchNode
     * @param {Node} node Node to check the format on.
     * @param {String} name Format name to check.
     * @param {Object} vars Optional list of variables to replace before checking it.
     * @param {Boolean} similar Match format that has similar properties.
     * @return {Object} Returns the format object it matches or undefined if it doesn't match.
     */
    matchNode: Fun.curry(MatchFormat.matchNode, editor),

    /**
     * Returns true/false if the specified format can be applied to the current selection or not. It
     * will currently only check the state for selector formats, it returns true on all other format types.
     *
     * @method canApply
     * @param {String} name Name of format to check.
     * @return {boolean} true/false if the specified format can be applied to the current selection/node.
     */
    canApply: Fun.curry(MatchFormat.canApply, editor),

    /**
     * Executes the specified callback when the current selection matches the formats or not.
     *
     * @method formatChanged
     * @param {String} formats Comma separated list of formats to check for.
     * @param {function} callback Callback with state and args when the format is changed/toggled on/off.
     * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
     */
    formatChanged: Fun.curry(FormatChanged.formatChanged, editor, formatChangeState),

    /**
     * Returns a preview css text for the specified format.
     *
     * @method getCssText
     * @param {String/Object} format Format to generate preview css text for.
     * @return {String} Css text for the specified format.
     * @example
     * var cssText1 = editor.formatter.getCssText('bold');
     * var cssText2 = editor.formatter.getCssText({inline: 'b'});
     */
    getCssText: Fun.curry(Preview.getCssText, editor)
  };
}
Example #26
0
const isRawNodeInTable = function (root, rawNode) {
  const node = Element.fromDom(rawNode);
  const isRoot = Fun.curry(Compare.eq, root);
  return PredicateFind.ancestor(node, ElementType.isTableCell, isRoot).isSome();
};
Example #27
0
const createStyleForm = function (editor: Editor) {
  const createColorPickAction = function () {
    const colorPickerCallback = getColorPickerCallback(editor);
    if (colorPickerCallback) {
      return function (evt) {
        return colorPickerCallback.call(
          editor,
          function (value) {
            evt.control.value(value).fire('change');
          },
          evt.control.value()
        );
      };
    }
  };

  return {
    title: 'Advanced',
    type: 'form',
    defaults: {
      onchange: Fun.curry(updateStyleField, editor)
    },
    items: [
      {
        label: 'Style',
        name: 'style',
        type: 'textbox',
        onchange: Fun.curry(updateAdvancedFields, editor)
      },
      {
        type: 'form',
        padding: 0,
        formItemDefaults: {
          layout: 'grid',
          alignH: ['start', 'right']
        },
        defaults: {
          size: 7
        },
        items: [
          {
            label: 'Border style',
            type: 'listbox',
            name: 'borderStyle',
            width: 90,
            onselect: Fun.curry(updateStyleField, editor),
            values: [
              { text: 'Select...', value: '' },
              { text: 'Solid', value: 'solid' },
              { text: 'Dotted', value: 'dotted' },
              { text: 'Dashed', value: 'dashed' },
              { text: 'Double', value: 'double' },
              { text: 'Groove', value: 'groove' },
              { text: 'Ridge', value: 'ridge' },
              { text: 'Inset', value: 'inset' },
              { text: 'Outset', value: 'outset' },
              { text: 'None', value: 'none' },
              { text: 'Hidden', value: 'hidden' }
            ]
          },
          {
            label: 'Border color',
            type: 'colorbox',
            name: 'borderColor',
            onaction: createColorPickAction()
          },
          {
            label: 'Background color',
            type: 'colorbox',
            name: 'backgroundColor',
            onaction: createColorPickAction()
          }
        ]
      }
    ]
  };
};
Example #28
0
const open = function (editor) {
  const dom = editor.dom;
  let tableElm, cellElm, rowElm, classListCtrl, data;
  const rows = [];
  let generalRowForm;

  tableElm = dom.getParent(editor.selection.getStart(), 'table');
  cellElm = dom.getParent(editor.selection.getStart(), 'td,th');

  Tools.each(tableElm.rows, function (row) {
    Tools.each(row.cells, function (cell) {
      if (dom.getAttrib(cell, 'data-mce-selected') || cell === cellElm) {
        rows.push(row);
        return false;
      }
    });
  });

  rowElm = rows[0];
  if (!rowElm) {
    // If this element is null, return now to avoid crashing.
    return;
  }

  if (rows.length > 1) {
    data = {
      height: '',
      scope: '',
      class: '',
      align: '',
      type: rowElm.parentNode.nodeName.toLowerCase()
    };
  } else {
    data = extractDataFromElement(editor, rowElm);
  }

  if (editor.settings.table_row_class_list) {
    classListCtrl = {
      name: 'class',
      type: 'listbox',
      label: 'Class',
      values: Helpers.buildListItems(
        editor.settings.table_row_class_list,
        function (item) {
          if (item.value) {
            item.textStyle = function () {
              return editor.formatter.getCssText({ block: 'tr', classes: [item.value] });
            };
          }
        }
      )
    };
  }

  generalRowForm = {
    type: 'form',
    columns: 2,
    padding: 0,
    defaults: {
      type: 'textbox'
    },
    items: [
      {
        type: 'listbox',
        name: 'type',
        label: 'Row type',
        text: 'Header',
        maxWidth: null,
        values: [
          { text: 'Header', value: 'thead' },
          { text: 'Body', value: 'tbody' },
          { text: 'Footer', value: 'tfoot' }
        ]
      },
      {
        type: 'listbox',
        name: 'align',
        label: 'Alignment',
        text: 'None',
        maxWidth: null,
        values: [
          { text: 'None', value: '' },
          { text: 'Left', value: 'left' },
          { text: 'Center', value: 'center' },
          { text: 'Right', value: 'right' }
        ]
      },
      { label: 'Height', name: 'height' },
      classListCtrl
    ]
  };

  if (editor.settings.table_row_advtab !== false) {
    editor.windowManager.open({
      title: 'Row properties',
      data,
      bodyType: 'tabpanel',
      body: [
        {
          title: 'General',
          type: 'form',
          items: generalRowForm
        },
        Helpers.createStyleForm(editor)
      ],
      onsubmit: Fun.curry(onSubmitRowForm, editor, rows)
    });
  } else {
    editor.windowManager.open({
      title: 'Row properties',
      data,
      body: generalRowForm,
      onsubmit: Fun.curry(onSubmitRowForm, editor, rows)
    });
  }
};
Example #29
0
  }
  return fontSize;
};

const normalizeFontFamily = (fontFamily: string) => {
  // 'Font name', Font -> Font name,Font
  return fontFamily.replace(/[\'\"\\]/g, '').replace(/,\s+/g, ',');
};

const getComputedFontProp = (propName: string, elm: HTMLElement): Option<string> => {
  return Option.from(DOMUtils.DOM.getStyle(elm, propName, true));
};

const getFontProp = (propName: string) => {
  return (rootElm: Element, elm: Node): string => {
    return Option.from(elm)
      .map(SugarElement.fromDom)
      .filter(SugarNode.isElement)
      .bind((element: any) => {
        return getSpecifiedFontProp(propName, rootElm, element.dom())
          .or(getComputedFontProp(propName, element.dom()));
      })
      .getOr('');
  };
};

export default {
  getFontSize: getFontProp('font-size'),
  getFontFamily: Fun.compose(normalizeFontFamily, getFontProp('font-family')) as (rootElm: Element, elm: Node) => string,
  toPt
};
 const enrichMenu = function (item) {
   return Merger.deepMerge(item, {
     isSelected: Fun.constant(false),
     getPreview: Fun.constant('')
   });
 };