Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ckeditor/ckeditor5-widget

Package Overview
Dependencies
Maintainers
1
Versions
703
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-widget - npm Package Compare versions

Comparing version 0.1.1 to 0.2.0

.eslintrc.js

16

CHANGELOG.md
Changelog
=========
## [0.2.0](https://github.com/ckeditor/ckeditor5-widget/compare/v0.1.1...v0.2.0) (2017-09-03)
### Bug fixes
* Added initial contenteditable state for editable widget. Closes [#9](https://github.com/ckeditor/ckeditor5-widget/issues/9). ([c6321ff](https://github.com/ckeditor/ckeditor5-widget/commit/c6321ff))
### Features
* <kbd>Ctrl</kbd>+<kbd>A</kbd> in a nested editable should select nested editable's content. Closes [#13](https://github.com/ckeditor/ckeditor5-widget/issues/13). ([35a8aff](https://github.com/ckeditor/ckeditor5-widget/commit/35a8aff))
### Other changes
* Adjusted widget to the editor read-only mode. Closes [#7](https://github.com/ckeditor/ckeditor5-widget/issues/7). ([2726873](https://github.com/ckeditor/ckeditor5-widget/commit/2726873))
* Introduced highlights support for widgets. Closes [#11](https://github.com/ckeditor/ckeditor5-widget/issues/11). ([0bd3d66](https://github.com/ckeditor/ckeditor5-widget/commit/0bd3d66))
## [0.1.1](https://github.com/ckeditor/ckeditor5-widget/compare/v0.1.0...v0.1.1) (2017-05-07)

@@ -5,0 +21,0 @@

11

gulpfile.js

@@ -6,3 +6,3 @@ /**

/* jshint browser: false, node: true, strict: true */
/* eslint-env node */

@@ -12,3 +12,4 @@ 'use strict';

const gulp = require( 'gulp' );
const ckeditor5Lint = require( '@ckeditor/ckeditor5-dev-lint' )( {
const ckeditor5Lint = require( '@ckeditor/ckeditor5-dev-lint' );
const options = {
// Files ignored by `gulp lint` task.

@@ -19,6 +20,6 @@ // Files from .gitignore will be added automatically during task execution.

]
} );
};
gulp.task( 'lint', ckeditor5Lint.lint );
gulp.task( 'lint-staged', ckeditor5Lint.lintStaged );
gulp.task( 'lint', () => ckeditor5Lint.lint( options ) );
gulp.task( 'lint-staged', () => ckeditor5Lint.lintStaged( options ) );
gulp.task( 'pre-commit', [ 'lint-staged' ] );
{
"name": "@ckeditor/ckeditor5-widget",
"version": "0.1.1",
"version": "0.2.0",
"description": "Widget API for CKEditor 5.",
"keywords": [],
"dependencies": {
"@ckeditor/ckeditor5-core": "^0.8.1",
"@ckeditor/ckeditor5-engine": "^0.10.0",
"@ckeditor/ckeditor5-utils": "^0.9.1",
"@ckeditor/ckeditor5-theme-lark": "^0.8.0"
"@ckeditor/ckeditor5-core": "^0.9.0",
"@ckeditor/ckeditor5-engine": "^0.11.0",
"@ckeditor/ckeditor5-utils": "^0.10.0",
"@ckeditor/ckeditor5-theme-lark": "^0.9.0"
},
"devDependencies": {
"@ckeditor/ckeditor5-dev-lint": "^2.0.2",
"@ckeditor/ckeditor5-dev-lint": "^3.1.0",
"eslint-config-ckeditor5": "^1.0.5",
"gulp": "^3.9.1",

@@ -15,0 +16,0 @@ "guppy-pre-commit": "^0.4.0"

CKEditor 5 widget API
========================================
[![Join the chat at https://gitter.im/ckeditor/ckeditor5](https://badges.gitter.im/ckeditor/ckeditor5.svg)](https://gitter.im/ckeditor/ckeditor5?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-widget.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-widget)

@@ -5,0 +6,0 @@ [![Build Status](https://travis-ci.org/ckeditor/ckeditor5-widget.svg)](https://travis-ci.org/ckeditor/ckeditor5-widget)

@@ -10,2 +10,4 @@ /**

import HighlightStack from './highlightstack';
const widgetSymbol = Symbol( 'isWidget' );

@@ -43,6 +45,7 @@ const labelSymbol = Symbol( 'label' );

* * adds `ck-widget` CSS class,
* * adds custom property allowing to recognize widget elements by using {@link ~isWidget}.
* * adds custom property allowing to recognize widget elements by using {@link ~isWidget},
* * implements `addHighlight` and `removeHighlight` custom properties to handle view highlight on widgets.
*
* @param {module:engine/view/element~Element} element
* @param {Object} [options]
* @param {Object} [options={}]
* @param {String|Function} [options.label] Element's label provided to {@link ~setLabel} function. It can be passed as

@@ -52,4 +55,3 @@ * a plain string or a function returning a string.

*/
export function toWidget( element, options ) {
options = options || {};
export function toWidget( element, options = {} ) {
element.setAttribute( 'contenteditable', false );

@@ -64,6 +66,42 @@ element.getFillerOffset = getFillerOffset;

setHighlightHandling(
element,
( element, descriptor ) => element.addClass( ...normalizeToArray( descriptor.class ) ),
( element, descriptor ) => element.removeClass( ...normalizeToArray( descriptor.class ) )
);
return element;
// Normalizes CSS class in descriptor that can be provided in form of an array or a string.
function normalizeToArray( classes ) {
return Array.isArray( classes ) ? classes : [ classes ];
}
}
/**
* Sets highlight handling methods. Uses {@link module:widget/highlightstack~HighlightStack} to
* properly determine which highlight descriptor should be used at given time.
*
* @param {module:engine/view/element~Element} element
* @param {Function} add
* @param {Function} remove
*/
export function setHighlightHandling( element, add, remove ) {
const stack = new HighlightStack();
stack.on( 'change:top', ( evt, data ) => {
if ( data.oldDescriptor ) {
remove( element, data.oldDescriptor );
}
if ( data.newDescriptor ) {
add( element, data.newDescriptor );
}
} );
element.setCustomProperty( 'addHighlight', ( element, descriptor ) => stack.add( descriptor ) );
element.setCustomProperty( 'removeHighlight', ( element, descriptor ) => stack.remove( descriptor ) );
}
/**
* Sets label for given element.

@@ -98,4 +136,5 @@ * It can be passed as a plain string or a function returning a string. Function will be called each time label is retrieved by

* Adds functionality to provided {module:engine/view/editableelement~EditableElement} to act as a widget's editable:
* * sets `contenteditable` attribute to `true`,
* * adds `ck-editable` CSS class,
* * sets `contenteditable` as `true` when {module:engine/view/editableelement~EditableElement#isReadOnly} is `false`
* otherwise set `false`,
* * adds `ck-editable_focused` CSS class when editable is focused and removes it when it's blurred.

@@ -107,5 +146,12 @@ *

export function toWidgetEditable( editable ) {
editable.setAttribute( 'contenteditable', 'true' );
editable.addClass( 'ck-editable' );
// Set initial contenteditable value.
editable.setAttribute( 'contenteditable', !editable.isReadOnly );
// Bind contenteditable property to element#isReadOnly.
editable.on( 'change:isReadOnly', ( evt, property, is ) => {
editable.setAttribute( 'contenteditable', !is );
} );
editable.on( 'change:isFocused', ( evt, property, is ) => {

@@ -112,0 +158,0 @@ if ( is ) {

@@ -18,6 +18,8 @@ /**

import { isWidget, WIDGET_SELECTED_CLASS_NAME, getLabel } from './utils';
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
import { keyCodes, getCode, parseKeystroke } from '@ckeditor/ckeditor5-utils/src/keyboard';
import '../theme/theme.scss';
const selectAllKeystrokeCode = parseKeystroke( 'Ctrl+A' );
/**

@@ -37,3 +39,3 @@ * The widget plugin.

static get pluginName() {
return 'widget/widget';
return 'Widget';
}

@@ -127,10 +129,18 @@

*/
_onKeydown( eventInfo, domEventData ) {
_onKeydown( eventInfo, domEventData ) {
const keyCode = domEventData.keyCode;
const isForward = keyCode == keyCodes.delete || keyCode == keyCodes.arrowdown || keyCode == keyCodes.arrowright;
let wasHandled = false;
// Checks if delete/backspace or arrow keys were handled and then prevents default event behaviour and stops
// event propagation.
if ( ( isDeleteKeyCode( keyCode ) && this._handleDelete( isForward ) ) ||
( isArrowKeyCode( keyCode ) && this._handleArrowKeys( isForward ) ) ) {
// Checks if the keys were handled and then prevents the default event behaviour and stops
// the propagation.
if ( isDeleteKeyCode( keyCode ) ) {
wasHandled = this._handleDelete( isForward );
} else if ( isArrowKeyCode( keyCode ) ) {
wasHandled = this._handleArrowKeys( isForward );
} else if ( isSelectAllKeyCode( domEventData ) ) {
wasHandled = this._selectAllNestedEditableContent();
}
if ( wasHandled ) {
domEventData.preventDefault();

@@ -220,2 +230,27 @@ eventInfo.stop();

/**
* Extends the {@link module:engine/model/selection~Selection document's selection} to span the entire
* content of the nested editable if already anchored in one.
*
* See: {@link module:engine/model/schema~Schema#getLimitElement}.
*
* @private
*/
_selectAllNestedEditableContent() {
const modelDocument = this.editor.document;
const modelSelection = modelDocument.selection;
const schema = modelDocument.schema;
const limitElement = schema.getLimitElement( modelSelection );
if ( modelSelection.getFirstRange().root == limitElement ) {
return false;
}
modelDocument.enqueueChanges( () => {
modelSelection.setIn( limitElement );
} );
return true;
}
/**
* Sets {@link module:engine/model/selection~Selection document's selection} over given element.

@@ -270,6 +305,6 @@ *

//Returns 'true' if provided key code represents one of the delete keys: delete or backspace.
// Returns 'true' if provided key code represents one of the delete keys: delete or backspace.
//
//@param {Number} keyCode
//@returns {Boolean}
// @param {Number} keyCode
// @returns {Boolean}
function isDeleteKeyCode( keyCode ) {

@@ -279,2 +314,10 @@ return keyCode == keyCodes.delete || keyCode == keyCodes.backspace;

// Returns 'true' if provided (DOM) key event data corresponds with the Ctrl+A keystroke.
//
// @param {module:engine/view/observer/keyobserver~KeyEventData} domEventData
// @returns {Boolean}
function isSelectAllKeyCode( domEventData ) {
return getCode( domEventData ) == selectAllKeystrokeCode;
}
// Returns `true` when element is a nested editable or is placed inside one.

@@ -281,0 +324,0 @@ //

@@ -15,2 +15,3 @@ /**

toWidgetEditable,
setHighlightHandling,
WIDGET_CLASS_NAME

@@ -33,3 +34,3 @@ } from '../src/utils';

it( 'should define getFillerOffset method', () => {
expect( element.getFillerOffset ).to.be.function;
expect( element.getFillerOffset ).to.be.a( 'function' );
expect( element.getFillerOffset() ).to.be.null;

@@ -43,3 +44,2 @@ } );

it( 'should add element\'s label if one is provided', () => {
element = new ViewElement( 'div' );
toWidget( element, { label: 'foo bar baz label' } );

@@ -51,3 +51,2 @@

it( 'should add element\'s label if one is provided as function', () => {
element = new ViewElement( 'div' );
toWidget( element, { label: () => 'foo bar baz label' } );

@@ -57,2 +56,36 @@

} );
it( 'should set default highlight handling methods', () => {
toWidget( element );
const set = element.getCustomProperty( 'addHighlight' );
const remove = element.getCustomProperty( 'removeHighlight' );
expect( typeof set ).to.equal( 'function' );
expect( typeof remove ).to.equal( 'function' );
set( element, { priority: 1, class: 'highlight', id: 'highlight' } );
expect( element.hasClass( 'highlight' ) ).to.be.true;
remove( element, { priority: 1, class: 'highlight', id: 'highlight' } );
expect( element.hasClass( 'highlight' ) ).to.be.false;
} );
it( 'should set default highlight handling methods - CSS classes array', () => {
toWidget( element );
const set = element.getCustomProperty( 'addHighlight' );
const remove = element.getCustomProperty( 'removeHighlight' );
expect( typeof set ).to.equal( 'function' );
expect( typeof remove ).to.equal( 'function' );
set( element, { priority: 1, class: [ 'highlight', 'foo' ], id: 'highlight' } );
expect( element.hasClass( 'highlight' ) ).to.be.true;
expect( element.hasClass( 'foo' ) ).to.be.true;
remove( element, { priority: 1, class: [ 'foo', 'highlight' ], id: 'highlight' } );
expect( element.hasClass( 'highlight' ) ).to.be.false;
expect( element.hasClass( 'foo' ) ).to.be.false;
} );
} );

@@ -113,2 +146,10 @@

it( 'should add proper contenteditable value when element is read-only', () => {
element.isReadOnly = false;
expect( element.getAttribute( 'contenteditable' ) ).to.true;
element.isReadOnly = true;
expect( element.getAttribute( 'contenteditable' ) ).to.false;
} );
it( 'should add proper class when element is focused', () => {

@@ -122,2 +163,101 @@ element.isFocused = true;

} );
describe( 'addHighlightHandling()', () => {
let element, addSpy, removeSpy, set, remove;
beforeEach( () => {
element = new ViewElement( 'p' );
addSpy = sinon.spy();
removeSpy = sinon.spy();
setHighlightHandling( element, addSpy, removeSpy );
set = element.getCustomProperty( 'addHighlight' );
remove = element.getCustomProperty( 'removeHighlight' );
} );
it( 'should set highlight handling methods', () => {
expect( typeof set ).to.equal( 'function' );
expect( typeof remove ).to.equal( 'function' );
} );
it( 'should call highlight methods when descriptor is added and removed', () => {
const descriptor = { priority: 10, class: 'highlight', id: 'highlight' };
set( element, descriptor );
remove( element, descriptor );
sinon.assert.calledOnce( addSpy );
sinon.assert.calledWithExactly( addSpy, element, descriptor );
sinon.assert.calledOnce( removeSpy );
sinon.assert.calledWithExactly( removeSpy, element, descriptor );
} );
it( 'should call highlight methods when next descriptor is added', () => {
const descriptor = { priority: 10, class: 'highlight', id: 'highlight-1' };
const secondDescriptor = { priority: 11, class: 'highlight', id: 'highlight-2' };
set( element, descriptor );
set( element, secondDescriptor );
sinon.assert.calledTwice( addSpy );
expect( addSpy.firstCall.args[ 1 ] ).to.equal( descriptor );
expect( addSpy.secondCall.args[ 1 ] ).to.equal( secondDescriptor );
} );
it( 'should not call highlight methods when descriptor with lower priority is added', () => {
const descriptor = { priority: 10, class: 'highlight', id: 'highlight-1' };
const secondDescriptor = { priority: 9, class: 'highlight', id: 'highlight-2' };
set( element, descriptor );
set( element, secondDescriptor );
sinon.assert.calledOnce( addSpy );
expect( addSpy.firstCall.args[ 1 ] ).to.equal( descriptor );
} );
it( 'should call highlight methods when descriptor is removed changing active descriptor', () => {
const descriptor = { priority: 10, class: 'highlight', id: 'highlight-1' };
const secondDescriptor = { priority: 11, class: 'highlight', id: 'highlight-2' };
set( element, descriptor );
set( element, secondDescriptor );
remove( element, secondDescriptor );
sinon.assert.calledThrice( addSpy );
expect( addSpy.firstCall.args[ 1 ] ).to.equal( descriptor );
expect( addSpy.secondCall.args[ 1 ] ).to.equal( secondDescriptor );
expect( addSpy.thirdCall.args[ 1 ] ).to.equal( descriptor );
sinon.assert.calledTwice( removeSpy );
expect( removeSpy.firstCall.args[ 1 ] ).to.equal( descriptor );
expect( removeSpy.secondCall.args[ 1 ] ).to.equal( secondDescriptor );
} );
it( 'should call highlight methods when descriptor is removed not changing active descriptor', () => {
const descriptor = { priority: 10, class: 'highlight', id: 'highlight-1' };
const secondDescriptor = { priority: 9, class: 'highlight', id: 'highlight-2' };
set( element, descriptor );
set( element, secondDescriptor );
remove( element, secondDescriptor );
sinon.assert.calledOnce( addSpy );
expect( addSpy.firstCall.args[ 1 ] ).to.equal( descriptor );
sinon.assert.notCalled( removeSpy );
} );
it( 'should call highlight methods - CSS class array', () => {
const descriptor = { priority: 10, class: [ 'highlight', 'a' ], id: 'highlight-1' };
const secondDescriptor = { priority: 10, class: [ 'highlight', 'b' ], id: 'highlight-2' };
set( element, descriptor );
set( element, secondDescriptor );
sinon.assert.calledTwice( addSpy );
expect( addSpy.firstCall.args[ 1 ] ).to.equal( descriptor );
expect( addSpy.secondCall.args[ 1 ] ).to.equal( secondDescriptor );
} );
} );
} );

@@ -25,5 +25,3 @@ /**

beforeEach( () => {
return VirtualTestEditor.create( {
plugins: [ Widget ]
} )
return VirtualTestEditor.create( { plugins: [ Widget ] } )
.then( newEditor => {

@@ -40,2 +38,3 @@ editor = newEditor;

doc.schema.registerItem( 'nested' );
doc.schema.limits.add( 'nested' );
doc.schema.allow( { name: '$inline', inside: 'nested' } );

@@ -175,3 +174,3 @@ doc.schema.allow( { name: 'nested', inside: 'widget' } );

it( 'fake selection should be empty if widget is not selected', () => {
setModelData( doc, '<widget>foo bar</widget>' );
setModelData( doc, '<paragraph>foo</paragraph><widget>foo bar</widget>' );

@@ -182,14 +181,14 @@ expect( viewDocument.selection.fakeSelectionLabel ).to.equal( '' );

it( 'should toggle selected class', () => {
setModelData( doc, '[<widget>foo</widget>]' );
setModelData( doc, '<paragraph>foo</paragraph>[<widget>foo</widget>]' );
expect( getViewData( viewDocument ) ).to.equal(
'[<div class="ck-widget ck-widget_selected" contenteditable="false">foo<b></b></div>]'
'<p>foo</p>[<div class="ck-widget ck-widget_selected" contenteditable="false">foo<b></b></div>]'
);
doc.enqueueChanges( () => {
doc.selection.collapseToStart();
doc.selection.removeAllRanges();
} );
expect( getViewData( viewDocument ) ).to.equal(
'[]<div class="ck-widget" contenteditable="false">foo<b></b></div>'
'<p>{}foo</p><div class="ck-widget" contenteditable="false">foo<b></b></div>'
);

@@ -450,3 +449,3 @@ } );

setModelData( doc, '<paragraph>foo[]</paragraph><widget></widget>' );
viewDocument.on( 'keydown', keydownHandler );
viewDocument.on( 'keydown', keydownHandler );

@@ -581,3 +580,3 @@ viewDocument.fire( 'keydown', domEventDataMock );

setModelData( doc, '<paragraph>foo</paragraph>[<widget></widget>]' );
viewDocument.on( 'keydown', keydownHandler );
viewDocument.on( 'keydown', keydownHandler );

@@ -598,3 +597,3 @@ viewDocument.fire( 'keydown', domEventDataMock );

setModelData( doc, '[<widget></widget>]<paragraph>foo</paragraph>' );
viewDocument.on( 'keydown', keydownHandler );
viewDocument.on( 'keydown', keydownHandler );

@@ -756,2 +755,18 @@ viewDocument.fire( 'keydown', domEventDataMock );

describe( 'Ctrl+A', () => {
test(
'should select the entire content of the nested editable',
'<widget><nested>foo[]</nested></widget><paragraph>bar</paragraph>',
{ keyCode: keyCodes.a, ctrlKey: true },
'<widget><nested>[foo]</nested></widget><paragraph>bar</paragraph>'
);
test(
'should not change the selection if outside of the nested editable',
'<widget><nested>foo</nested></widget><paragraph>[]bar</paragraph>',
{ keyCode: keyCodes.a, ctrlKey: true },
'<widget><nested>foo</nested></widget><paragraph>[]bar</paragraph>'
);
} );
function test( name, data, keyCodeOrMock, expected ) {

@@ -758,0 +773,0 @@ it( name, () => {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc