Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Unit tests from API Documentation #49

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,58 @@ ArteJS [![Build Status](https://secure.travis-ci.org/vistaprint/ArteJS.png?branc
ArteJS is a powerful, extensible, configurable, flexible and cross-browser rich text editor with a simple API that produces consistent, valid and concise html.

[ArteJS Examples / Documentation](http://vistaprint.github.io/ArteJS/ "ArteJS Examples / Documentation")

### Options

You can initialize your Arte editor with some custom options, passing a literal object in the first argument.

```js
$("#editor").Arte(options);
```

The available options are listed below with their default values:

```js
$("#editor").Arte({

// Set of initial styles applied to rich text editor
styles: {
"min-height": "200px",
"height": "inherit"
},

// Collection of classes applied to rich text editor
classes: [],

// Initial value of the text editor
value: "Please enter text ...",

// Editor Type: plain text or rich text
editorType: "plainText",

// attach event handlers
on: null
});
```

### Commands

After you initialize your Arte editor, you can call commands:

```js
var arte = $("#editor").Arte(options);

// make the selection bold
arte.Arte("bold");

// set the fontsize of the selection to 12px
arte.Arte("fontSize", "12px");

// Alternative:

arte.each(function() {
this.bold();
this.fontSize("12px");
});

```
18 changes: 12 additions & 6 deletions src/editor/core/Arte.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
/**
/**
* @fileoverview jQuery wrapper around the Rich text editor
* Usage:
* 1) $(selector).Arte()
* Converts the matched elements into rich text editor using default options or returns and existing instance
* @returns {jQuery selector of Arte.TextArea}
* 2) $(selector).Arte({ options });
* Converts the matched elements into rich text editor using the options supplied or returns and existing instance
* @returns {jQuery selector of Arte.TextArea}
* 3) $(selector).Arte(command, arguments)
* Execute a rich text command with arguments
* @returns {jQuery selector of return value of commands called}
*
*/
(function($) {
$.Arte = $.Arte || {};
$.fn.Arte = function(options, args) {
var result = [];
rangy.init();
this.each(function() {

if (!this.length) {
return this;
}
return this.map(function() {
var $this = $(this);
var editor = $this.data("Arte");
if (options && typeof(options) === "string") {
Expand All @@ -28,7 +35,7 @@
}

var returnValue = editor[methodName].call(editor, args);
result.push(returnValue);
return returnValue;
} else {
// If $this is not a rich text editor, construct the editor
if (!editor) {
Expand All @@ -37,9 +44,8 @@
editor = new $.Arte.TextArea(options);
$this.data("Arte", editor);
}
result.push(editor);
return editor;
}
});
return $(result);
};
})(jQuery);
60 changes: 52 additions & 8 deletions src/editor/core/Commands.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// @dependencies: Arte.js, TextArea.js
/// @dependencies: Arte.js, TextArea.js
/**
* @fileoverview extends Arte prototype to add rich text commands
*/
Expand All @@ -11,12 +11,20 @@
var commandAttr = constants.commandAttrType;
/**
* Determine the command attribute using the options
* @param {String} commandName - name of the command get attrType for
* @param {Object} options
* @param {Object} [options.commandAttrType] - pass a commandAttrType in the options if you want to use it
* @param {Object} [options.styleName] - will use styleName as the attrType if you pass one in
* @param {Object} [options.className] - will use className as the attrType if you pass one in
* @param {Object} [options.tagName] - will use tagName as the attrType if you pass one in
* @return {String} returns the attrType in this order
* 1. If it was defined in the config for the command use that
* 2. If no options object then return commandAttrType object from configuration
* 3. If options defines a commandAttrType use that
* 4. If a styleName/className/tagName is passed in options use the styleName/className/tagName
* properties from $.Arte.constants as the attrType
*/
var commandAttrType = function(commandName, options) {
// commandAttrType is selected based on the following precedence
// 1) As defined in the options
// 2) Infer from the options
// 3) Use default
var commandConfig = configuration.commands[commandName];
if (commandConfig.commandAttrType) {
return commandConfig.commandAttrType;
Expand All @@ -41,16 +49,18 @@
return attrType;
};

/*
/**
* Executes a rich text command
* @param {String} commandName - name of the command to call
* @param {Object} options
*/
var exec = function(commandName, options) {
var commandOptions = constructCommandOptions.call(this, commandName, options);

commandOptions.execute = true;
this.triggerEvent(constants.eventNames.onbeforecommand, commandOptions);

if (!commandOptions.execute) { // The client requested the cancelation of this command
if (!commandOptions.execute) { // The client requested the cancellation of this command
return;
}

Expand All @@ -59,6 +69,17 @@
this.triggerEvent(constants.eventNames.oncommand, commandOptions);
};

/**
* Get the correct command value based on the given parameters
* @param {String} commandName - name of the command get the value for
* @param {String} attrType - attrType you are using the command with
* @param {Object|String} options - either an options object or it can just be a string containing the commandValue
* @param {Object} [options.styleValue] - if attrType == styleName and you pass this in then styleValue will be the the commandValue
* @param {Object} [options.className] - if attrType == className and you pass this in then className will be the the commandValue
* @param {Object} [options.commandValue] - if you pass in a commandValue that will be returned
* @return {String} try to return commandValue using options that were passed in,
* if none are found return the default value for the command specified in the configuration
*/
var getCommandValueOrDefault = function(commandName, attrType, options) {
if (options && attrType === commandAttr.styleName && options.styleValue) {
return options.styleValue;
Expand All @@ -79,6 +100,15 @@
}
};

/**
* Get the tagName for the given command
* @param {String} commandName - name of the command get the tagName for
* @param {String} attrType - attrType you are using the command with - used to find the right tag
* (for example bold uses the B tag if we want to apply tags or a SPAN if applying styles/classes)
* @param {Object} options
* @param {String} options.tagName - if you already know the tagName and put it in the options it will be returned back
* @return {String} return tagName for the given command or if there is none just return default inline/block tag based on command type
*/
var getTagNameOrDefault = function(commandName, attrType, options) {
if (options && options.tagName) {
return options.tagName;
Expand All @@ -99,6 +129,12 @@
configuration.defaultInlineTag : configuration.defaultBlockTag;
};

/**
* Creates command options to use for the command based on the configuration
* @param {String} commandName - name of the command generate the options for
* @param {Object} options - a object containing various options
* @return {Object} return tagName for the given command or if there is none just return default inline/block tag based on command type
*/
var constructCommandOptions = function(commandName, options) {
var attr = commandAttrType(commandName, options);
var commandConfig = configuration.commands[commandName];
Expand Down Expand Up @@ -184,7 +220,15 @@
"textAlign": function(options) {
exec.apply(this, ["textAlign", options]);
},
// Apply the styles/classes to the content editable element
/**
* Toggles styles
* @param {Object} [options]
* @param {jQuery} [options.element] - pass in your own element or use the rich text area by default
* @param {Object} [options.styleName] - name of style you would like to toggle on the element (e.g. font-weight)
* @param {Object} [options.styleValue] - value for the styleName defined above
* that you would like to toggle on the element (e.g. bold)
* @param {Object} [options.className] - name of css class you would like to toggle on the element (e.g. arte-font-weight)
*/
"toggleStyleOnElement": function(options) {
var element = options.element || this.$el;
if (options && options.styleName) {
Expand Down
4 changes: 2 additions & 2 deletions src/editor/core/Configuration.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* This file lists the configuration and constants used by ArteJS
*/
(function($) {
Expand Down Expand Up @@ -113,7 +113,7 @@
commandAttrType: constants.commandAttrType.styleName,

/*
* An attributed added to the dom element to identify that dom element as rich text field
* An attribute added to the dom element to identify that dom element as rich text field
*/
textFieldIdentifier: "rteTextField",

Expand Down
8 changes: 4 additions & 4 deletions src/editor/core/PluginManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
register: function(name, plugin) {
this.plugins[name] = plugin;
},
/*
/**
* Initializes the plugin
* @param {Arte} an instance of Arte
*/
init: function(richTextEditor) {
richTextEditor.pluginInstances = richTextEditor.pluginInstances || [];
for (var pluginName in this.plugins) {
var pluginInstanse = new this.plugins[pluginName]();
pluginInstanse.init(richTextEditor);
richTextEditor.pluginInstances.push(pluginInstanse);
var pluginInstance = new this.plugins[pluginName]();
pluginInstance.init(richTextEditor);
richTextEditor.pluginInstances.push(pluginInstance);
}
}
};
Expand Down
48 changes: 40 additions & 8 deletions src/editor/core/TextArea.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// dependencies: Arte.js
/// dependencies: Arte.js
(function($) {
$.Arte = $.Arte || {};
$.Arte.TextArea = function(options) {
Expand Down Expand Up @@ -70,7 +70,7 @@
}
me._container.appendChild(me.el);
me.$el = $(me.el);
// Use an existing DIV or TEXTAREA if it already exists
// Use an existing DIV or TEXTAREA if it already exists
} else {
me.el = me._container.childNodes[0];
if (me.el.tagName === "DIV") {
Expand Down Expand Up @@ -170,30 +170,42 @@
};

$.extend($.Arte.TextArea.prototype, {
// Get innerHtml of the contentEditable element
/**
* Get or Set innerHtml of the contentEditable element
* @params {string} value string to set innerHTML of element to
* @returns {string} returns 'innerHTML' of the contentEditable element
* if in rich text mode or 'value' of the element if in plaintext mode
* if getting value, otherwise returns nothing
*/
"value": function(value) {
var constants = $.Arte.constants;
var prop = this.editorType === constants.editorTypes.richText ? "innerHTML" : "value";
var currentValue = this.el[prop];

if (typeof(value) === "undefined") {
if (typeof (value) === "undefined") {
return currentValue;
}

if (currentValue === value) {
return;
}

//TODO should we return this so that we can chain things?
this.el[prop] = value;
this._currentOuterValue = this._container.innerHTML;
this.triggerEvent(constants.eventNames.onvaluechange, {
newValue: value,
src: "external"
});
},
// Get outerHtml of the contentEditable element

/**
* Gets or sets outerHtml of the contentEditable element
* @params {string} value html string to set outerHTML of element to
* @returns {string} returns 'outerHTML' of the contentEditable element with contentEditable tag removed
*/
"outerValue": function(value) {
if (typeof(value) === "undefined") {
if (typeof (value) === "undefined") {
var clone = this.$element.clone();
clone.children().removeAttr("contenteditable");
return clone.html();
Expand All @@ -204,6 +216,11 @@
this.el.setAttribute("class", newElement.getAttribute("class") || "");
this.value(newElement.innerHTML);
},

/**
* Calls a focus event on the contentEditable element, moves the cursor
* to the end of that element, and fires an onselectionchange event
*/
"focus": function() {
var me = this;
var focusHandler = function() {
Expand All @@ -214,13 +231,23 @@
me.$el.on("focus", focusHandler);
me.$el.focus();
},

/**
* Triggers the event passed in on the contentEditable element with data provided
* @params {string} name - name of the event you want to trigger
* @params {object} data - extra parameters to pass into the event handler
*/
"triggerEvent": function(name, data) {
this.$element.trigger(name, $.extend(data, {
textArea: this
}));
},

/**
* Removes Arte from this element (Converts the rich text editor to non-editable state and remove rich text state information)
* @params {Object} options - pass in 'removeContent' in options object to also clear the element of all text and formatting
*/
"destroy": function(options) {
// Converts the rich text editor to non-editable state and remove rich text state information
this.$element.removeData("Arte");
this.$element.removeAttr($.Arte.configuration.textFieldIdentifier);
this.$element.off();
Expand All @@ -233,12 +260,17 @@
this.$element.empty();
}
},

/**
* on/off methods to support attaching events handler using a rich text instance
* Listen to events on the Arte.TextAreas element (same as adding events directly to the element)
*/
on: function(type, handler) {
this.$element.on(type, handler);
},

/**
* Stop listening to events on the Arte.TextAreas element (same as adding events directly to the element)
*/
off: function(type, handler) {
this.$element.off(type, handler);
}
Expand Down
Loading