Created
February 7, 2013 16:26
-
-
Save hadrienl/4732118 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* WikiDOM tools | |
* @module ob/plugins/wikidom/wikidom | |
*/ | |
var NS = 'ob.plugins.wikidom'; | |
CHILD_PARAGRAPH = 'paragraph', | |
CHILD_HEADING = 'heading', | |
CHILD_PRE = 'pre', | |
CHILD_LIST = 'list', | |
CHILD_LIST_ITEM = 'listItem', | |
ANNOTATION_STRONG = 'textStyle/strong', | |
ANNOTATION_EM = 'textStyle/emphasize', | |
ANNOTATION_DEL = 'textStyle/delete', | |
ANNOTATION_LINK = 'link/external', | |
LIST_TYPE_BULLET = 'bullet', | |
LIST_TYPE_NUMBER = 'number', | |
CHILD_TYPES = [CHILD_PARAGRAPH, CHILD_HEADING, CHILD_PRE, CHILD_LIST, CHILD_LIST_ITEM], | |
CHILD_TYPES_CFG = [{ | |
tagNames: ['p'] | |
}, { | |
tagNames: ['h1', 'h2', 'h3'], | |
attributes: [{ | |
level: 1 | |
}, { | |
level: 2 | |
}, { | |
level: 3 | |
}] | |
}, { | |
tagNames: ['pre'] | |
}, { | |
tagNames: ['ul', 'ol'], | |
children: [CHILD_LIST_ITEM] | |
}, { | |
tagNames: ['li'], | |
children: [CHILD_LIST, CHILD_PARAGRAPH] | |
}], | |
ANNOTATIONS_TYPES = [ANNOTATION_STRONG, ANNOTATION_EM, ANNOTATION_DEL, ANNOTATION_LINK], | |
ANNOTATIONS_TYPES_CFG = [{ | |
tagNames: ['strong', 'b'], | |
hasData: false | |
}, { | |
tagNames: ['em', 'i'], | |
hasData: false | |
}, { | |
tagNames: ['del', 'strike'], | |
hasData: false | |
}, { | |
tagNames: ['a'], | |
hasData: true | |
}], | |
/** | |
* Annotation object | |
* @class Annotation | |
* @namespace Y.ob.plugins.wikidom | |
* @extends Y.Base | |
*/ | |
Annotation = Y.Base.create('Annotation', Y.Base, [], { | |
asJson: function() | |
{ | |
var annotation = { | |
type: this.get('type'), | |
range: this.get('range') | |
}, | |
data = this.get('data'); | |
if (data) | |
{ | |
annotation.data = data; | |
} | |
return annotation; | |
}, | |
/** | |
* Tell if this annotation need data attribute | |
*/ | |
hasData: function() | |
{ | |
var cfg = ANNOTATIONS_TYPES_CFG[ | |
Y.Array.indexOf(ANNOTATIONS_TYPES, this.get('type'))]; | |
return cfg && cfg.hasData; | |
}, | |
/** | |
* Get the HTML tag corresponding to this annotation | |
* @method getTag | |
* | |
*/ | |
getTag: function(begin) | |
{ | |
var type = this.get('type'), | |
tag = '<'; | |
if (!begin) | |
{ | |
tag += '/'; | |
} | |
tag += ANNOTATIONS_TYPES_CFG[ | |
Y.Array.indexOf(ANNOTATIONS_TYPES, type) | |
].tagNames[0]; | |
if (begin && | |
ANNOTATION_LINK === type) | |
{ | |
tag += ' href="'; | |
tag += this.get('data.title'); | |
tag +='"'; | |
} | |
tag += '>'; | |
return tag; | |
} | |
}, { | |
ATTRS: { | |
type: { | |
validator: function(v) | |
{ | |
return ANNOTATIONS_TYPES.indexOf(v) > -1; | |
} | |
}, | |
range: { | |
getter: function() | |
{ | |
return { | |
start: this.get('range_start'), | |
end: this.get('range_end') | |
} | |
} | |
}, | |
range_start: { | |
value: 0, | |
validator: function(v) | |
{ | |
return Y.Lang.isNumber(v); | |
} | |
}, | |
range_end: { | |
value: 0, | |
validator: function(v) | |
{ | |
return Y.Lang.isNumber(v); | |
} | |
}, | |
data: { | |
value: null, | |
validator: function(v) | |
{ | |
if (ANNOTATION_LINK === this.get('type') && | |
Y.Lang.isObject(v) && | |
v.title) | |
{ | |
return true; | |
} | |
return false; | |
} | |
} | |
} | |
}), | |
/** | |
* Content object | |
* @class Content | |
* @namespace Y.ob.plugins.wikidom | |
* @extends Y.Base | |
*/ | |
Content = Y.Base.create('Content', Y.Base, [], { | |
addAnnotation: function(a) | |
{ | |
if (!a.name) | |
{ | |
a = new Annotation(a); | |
} | |
this.get('annotations').push(a); | |
}, | |
asJson: function() | |
{ | |
var content = { | |
text: this.get('text') | |
}, | |
annotations = []; | |
Y.each( | |
this.get('annotations'), | |
function(a) | |
{ | |
annotations.push(a.asJson()); | |
} | |
); | |
if (annotations.length) | |
{ | |
content.annotations = annotations; | |
} | |
return content; | |
} | |
}, { | |
ATTRS: { | |
text: { | |
value: '', | |
validator: function(v) | |
{ | |
return Y.Lang.isString(v); | |
} | |
}, | |
annotations: { | |
valueFn: function() | |
{ | |
return []; | |
}, | |
setter: function(v) | |
{ | |
var annotations = this.get('annotations'); | |
if (!Y.Lang.isArray(v)) | |
{ | |
v = [v]; | |
} | |
Y.each( | |
v, | |
function(a) | |
{ | |
a = new Annotation({ | |
type: a.type, | |
data: a.data, | |
range_start: a.range.start, | |
range_end: a.range.end | |
}); | |
annotations.push(a); | |
}, | |
this | |
); | |
return annotations; | |
} | |
} | |
} | |
}), | |
/** | |
* Document object : base of WikiDOM object | |
* @class Document | |
* @namespace Y.ob.plugins.wikidom | |
* @extends Y.Base | |
*/ | |
Document = Y.Base.create('Document', Y.Base, [], { | |
addChild: function(child) | |
{ | |
if (!child.name || | |
child.name !== 'Child') | |
{ | |
child = new Child(child); | |
} | |
if (CHILD_LIST === this.get('type')) | |
{ | |
child.set('attributes.styles', this.get('attributes.styles')); | |
} | |
this.get('children').push(child); | |
return child; | |
}, | |
addChildren: function(children) | |
{ | |
var ret = []; | |
if (!Y.Lang.isArray(children)) | |
{ | |
children = [children]; | |
} | |
Y.each(children, function(child) | |
{ | |
ret.push( | |
this.addChild(child) | |
); | |
}, this); | |
return ret; | |
}, | |
/** | |
* Return Document transformed in a JSON chain | |
* @return json | |
*/ | |
asJson: function() | |
{ | |
var doc = { | |
type: this.get('type'), | |
children: [] | |
}; | |
Y.each( | |
this.get('children'), | |
function(c) | |
{ | |
doc.children.push(c.asJson()); | |
} | |
); | |
return doc; | |
}, | |
hasChild: function() | |
{ | |
return [CHILD_PARAGRAPH, CHILD_HEADING, CHILD_PRE, CHILD_LIST]; | |
}, | |
canHaveChild: function(tag) | |
{ | |
var type, children; | |
Y.some( | |
CHILD_TYPES_CFG, | |
function(c, i) | |
{ | |
if (Y.Array.indexOf(c.tagNames, tag) > -1) | |
{ | |
type = CHILD_TYPES[i]; | |
return true; | |
} | |
} | |
); | |
children = this.hasChild(); | |
return children && Y.Array.indexOf(children, type) > -1; | |
} | |
}, { | |
ATTRS: { | |
type: { | |
value: 'document', | |
readOnly: true | |
}, | |
children: { | |
valueFn: function() | |
{ | |
return []; | |
}, | |
setter: function(v) | |
{ | |
// to review | |
var children = []; | |
Y.each(v, function(c) | |
{ | |
if (!c) | |
{ | |
return; | |
} | |
if (!c.name || 'Child' !== c.name) | |
{ | |
c = new Child(c); | |
} | |
children.push(c); | |
}); | |
return children; | |
} | |
} | |
} | |
}), | |
/** | |
* Child object | |
* @class Child | |
* @namespace Y.ob.plugins.wikidom | |
* @extends Y.ob.plugins.wikidom.Document | |
*/ | |
Child = Y.Base.create('Child', Document, [], { | |
initializer: function(config) | |
{ | |
if (config && config.content) | |
{ | |
this.get('content').setAttrs(config.content); | |
} | |
}, | |
asJson: function() | |
{ | |
var child = { | |
type: this.get('type'), | |
content: this.get('content').asJson() | |
}, | |
children = [], | |
attributes = this.get('attributes'); | |
Y.each( | |
this.get('children'), | |
function(c) | |
{ | |
children.push(c.asJson()); | |
} | |
); | |
if (children.length) | |
{ | |
child.children = children; | |
} | |
if (attributes && Y.Object.keys(attributes).length) | |
{ | |
if (attributes.styles && | |
CHILD_LIST_ITEM !== child.type) | |
{ | |
delete attributes.styles; | |
} | |
} | |
if (attributes && Y.Object.keys(attributes).length) | |
{ | |
child.attributes = attributes; | |
} | |
return child; | |
}, | |
hasChild: function() | |
{ | |
var cfg = CHILD_TYPES_CFG[ | |
Y.Array.indexOf(CHILD_TYPES, this.get('type'))]; | |
return cfg && cfg.children; | |
}, | |
getTag: function() | |
{ | |
var type = this.get('type'), | |
children = this.get('children'), | |
tag, listtype; | |
switch (type) | |
{ | |
case CHILD_PARAGRAPH: | |
tag = 'p'; | |
break; | |
case CHILD_HEADING: | |
tag = 'h'; | |
tag += this.get('attributes.level'); | |
break; | |
case CHILD_PRE: | |
tag = 'pre'; | |
break; | |
case CHILD_LIST: | |
if (children.length) | |
{ | |
listtype = children[0].get('attributes.styles'); | |
listtype = listtype ? listtype[0] : null; | |
} | |
else | |
{ | |
listtype = this.get('attributes.styles'); | |
if (listtype) | |
{ | |
listtype = listtype[0]; | |
} | |
} | |
if (LIST_TYPE_NUMBER === listtype) | |
{ | |
tag = 'ol'; | |
} | |
else | |
{ | |
tag = 'ul'; | |
} | |
break; | |
case CHILD_LIST_ITEM: | |
tag = 'li'; | |
break; | |
} | |
return tag; | |
} | |
}, { | |
ATTRS: { | |
type: { | |
value: CHILD_PARAGRAPH, | |
validator: function(v) | |
{ | |
return CHILD_TYPES.indexOf(v) > -1; | |
}, | |
readOnly: false | |
}, | |
content: { | |
valueFn: function() | |
{ | |
return new Content(); | |
}, | |
readOnly: true | |
}, | |
attributes: { | |
valueFn: function(v) | |
{ | |
return { | |
level: null, | |
styles: null | |
}; | |
}, | |
setter: function(v, key) | |
{ | |
var children; | |
if ('attributes.styles' === key) | |
{ | |
if (!Y.Lang.isArray(v.styles)) | |
{ | |
v.styles = [v.styles]; | |
} | |
if (CHILD_LIST === this.get('type')) | |
{ | |
children = this.get('children'); | |
Y.each( | |
children, | |
function(c) | |
{ | |
c.set(key, v.styles); | |
} | |
); | |
} | |
} | |
if (!Y.Lang.isObject(v)) | |
{ | |
v = {}; | |
} | |
return v; | |
} | |
} | |
} | |
}); | |
/** | |
* Get an annotation object corresponding to a given tag name | |
* @method createAnnotationFromTag | |
* @param {String} tag HTML tag which will be determine the Annotation type | |
* @class Annotation | |
* @static | |
*/ | |
Annotation.createAnnotationFromTag = function(tag) | |
{ | |
var index; | |
Y.some( | |
ANNOTATIONS_TYPES_CFG, | |
function(c, i) | |
{ | |
if (Y.Array.indexOf(c.tagNames, tag) > -1) | |
{ | |
index = i; | |
return true; | |
} | |
} | |
); | |
if (Y.Lang.isNull(index)) | |
{ | |
return; | |
} | |
return new Annotation({ | |
type: ANNOTATIONS_TYPES[index] | |
}); | |
}; | |
/** | |
* Get a regexp fragment with all html tag which can be an annotation | |
* @method getTagsRegExp | |
* @class Annotation | |
* @static | |
*/ | |
Annotation.getTagsRegExp = function() | |
{ | |
return 'a|strong|b|em|i|del|strike'; | |
}; | |
/** | |
* Create a new Child object from a html tag | |
* @method getTagsRegExp | |
* @param {String} tag HTML tag which will specify the Child type | |
* @class Child | |
* @static | |
*/ | |
Child.createChildFromTag = function(tag) | |
{ | |
var index = null, | |
attributes = {}, | |
tagindex = null; | |
Y.some( | |
CHILD_TYPES_CFG, | |
function(c, i) | |
{ | |
tagindex = Y.Array.indexOf(c.tagNames, tag); | |
if (tagindex > -1) | |
{ | |
index = i; | |
return true; | |
} | |
} | |
); | |
if ('ul' === tag) | |
{ | |
attributes.styles = [LIST_TYPE_BULLET]; | |
} | |
if ('ol' === tag) | |
{ | |
attributes.styles = [LIST_TYPE_NUMBER]; | |
} | |
if (Y.Lang.isNull(index)) | |
{ | |
return null; | |
} | |
if (CHILD_TYPES_CFG[index].attributes) | |
{ | |
attributes = Y.mix( | |
CHILD_TYPES_CFG[index].attributes[tagindex], | |
attributes | |
); | |
} | |
return new Child({ | |
type: CHILD_TYPES[index], | |
attributes: attributes | |
}); | |
}; | |
/** | |
* Get a regexp fragment with all html tag which can be a child | |
* @method getTagsRegExp | |
* @class Child | |
* @static | |
*/ | |
Child.getTagsRegExp = function() | |
{ | |
return 'pre|p|h[1-3]|ul|ol|li'; | |
}; | |
/** | |
* Export classes and constants | |
*/ | |
Y.each( | |
{ | |
CHILD_PARAGRAPH: CHILD_PARAGRAPH, | |
CHILD_HEADING: CHILD_HEADING, | |
CHILD_PRE: CHILD_PRE, | |
CHILD_LIST: CHILD_LIST, | |
CHILD_LIST_ITEM: CHILD_LIST_ITEM, | |
ANNOTATION_STRONG: ANNOTATION_STRONG, | |
ANNOTATION_EM: ANNOTATION_EM, | |
ANNOTATION_DEL: ANNOTATION_DEL, | |
ANNOTATION_LINK: ANNOTATION_LINK, | |
LIST_TYPE_BULLET: LIST_TYPE_BULLET, | |
LIST_TYPE_NUMBER: LIST_TYPE_NUMBER, | |
Annotation: Annotation, | |
Content: Content, | |
Document: Document, | |
Child: Child | |
}, | |
function(c, n) | |
{ | |
Y.namespace(NS)[n] = c; | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment