}
function is_display_block (el) {
- if (el.currentStyle != null) {
- return el.currentStyle.display === 'block'
- } else {
+ if (el.nodeType === 1) {
return window.getComputedStyle(el, null).getPropertyValue('display') === 'block'
}
+ return false
}
// Pass return value from dom event handlers to this.
})
}
+// no closing tag, cannot have children
void_elements = {
- area: true,
- base: true,
- br: true,
- col: true,
- embed: true,
- hr: true,
- img: true,
- input: true,
- keygen: true,
- link: true,
- meta: true,
- param: true,
- source: true,
- track: true,
- wbr: true
+ area: 1,
+ base: 1,
+ br: 1,
+ col: 1,
+ embed: 1,
+ hr: 1,
+ img: 1,
+ input: 1,
+ keygen: 1,
+ link: 1,
+ meta: 1,
+ param: 1,
+ source: 1,
+ track: 1,
+ wbr: 1
+}
+
+// contents are not html encoded
+var plaintext_elements = {
+ style: 1,
+ script: 1,
+ xmp: 1,
+ iframe: 1,
+ noembed: 1,
+ noframes: 1,
+ plaintext: 1,
+ noscript: 1
+}
+
+// parser deletes a starting newline inside:
+var newline_eating_elements = {
+ pre: 1,
+ textarea: 1,
+ listing: 1
}
// this does not pretty-print
}
break
case 'text':
- ret += enc_text(n.text)
+ if (n.parent != null ? plaintext_elements[n.parent.name] : false) {
+ ret += n.text
+ } else if (n.parent != null ? newline_eating_elements[n.parent.name] && n.text.charAt(0) === "\n" : false) {
+ ret += enc_text("\n" + n.text)
+ } else {
+ ret += enc_text(n.text)
+ }
break
case 'comment':
ret += "<!--" + n.text + "-->" // TODO encode?
break
case 'tag':
if (c.name === 'script' || c.name === 'object' || c.name === 'iframe' || c.name === 'link') {
- // TODO put placeholders instead
- remove.unshift(i)
+ // TODO make some placeholders visible
+ // problematic to have different type than c: c.el = parent.ownerDocument.createComment(c.name + ' tag here')
+ c.el = parent.ownerDocument.createElement(c.name)
+ // correct type, but empty and no attributes
continue
}
- // TODO create in correct namespace
- c.el = parent.ownerDocument.createElement(c.name)
+ if (c.namespace === 'svg') {
+ c.el = parent.ownerDocument.createElementNS('http://www.w3.org/2000/svg', c.name)
+ } else if (c.namespace === 'mathml') {
+ c.el = parent.ownerDocument.createElementNS('http://www.w3.org/1998/Math/MathML', c.name)
+ } else {
+ c.el = parent.ownerDocument.createElement(c.name)
+ }
ref1 = c.attrs
for (k in ref1) {
v = ref1[k]
}
}
}
- results = []
- for (i = 0; i < remove.length; i++) {
- // FIXME this deletes the wrong node when siblings are removed
- index = remove[i]
- results.push(tree.splice(index, 1))
- }
- return results
}
function traverse_tree (tree, cb) {
// on_init: callback for when the editable content is in place
var css, opt_fragment, outer_bounds, outer_iframe_style, outer_wrap
this.options = options != null ? options : {}
+ this.pretty_print = options.pretty_print != null ? options.pretty_print : true
this.in_el = in_el
this.tree = null // array of Nodes, all editable content
this.tree_parent = null // this.tree is this.children. .el might === this.idoc.body
this.init_1_called = false // when iframes have loaded
this.outer_iframe // iframe to hold editor
this.outer_idoc // "document" object for this.outer_iframe
+ this.input_el = null
this.wrap2 = null // scrollbar is on this
this.wrap2_offset = null
this.wrap2_height = null // including padding
this.cursor_el = null
this.cursor_visible = false
this.cursor_ideal_x = null
- this.poll_for_blur_timeout = null
opt_fragment = this.options.fragment != null ? this.options.fragment : true
this.parser_opts = {}
if (opt_fragment) {
if (this.options.editor_id != null) {
this.outer_iframe.setAttribute('id', this.options.editor_id)
}
- this.outer_iframe.onload = (function(_this) {
- return function() {
- var icss
- _this.outer_idoc = _this.outer_iframe.contentDocument
- icss = domify(_this.outer_idoc, { style: { children: [
- domify(_this.outer_idoc, {text: css})
- ]}})
- _this.outer_idoc.head.appendChild(icss)
- _this.iframe = domify(_this.outer_idoc, {iframe: {sandbox: 'allow-same-origin allow-scripts'}})
- _this.iframe.onload = function() {
+ this.outer_iframe.onload = (function(_this) { return function() {
+ var icss
+ _this.outer_idoc = _this.outer_iframe.contentDocument
+ icss = domify(_this.outer_idoc, { style: { children: [
+ domify(_this.outer_idoc, {text: css})
+ ]}})
+ _this.outer_idoc.head.appendChild(icss)
+ _this.input_el = domify(_this.outer_idoc, {textarea: {style: "position: relative; left: 100px"}}) // {style: "display: block; position: absolute; top: -1000px; left: 0; height: 500px; width 50em"}})
+ _this.input_el.onblur = _this.onblur.bind(_this)
+ _this.outer_idoc.body.appendChild(_this.input_el)
+ _this.iframe = domify(_this.outer_idoc, {iframe: {sandbox: 'allow-same-origin allow-scripts'}})
+ _this.iframe.onload = function() {
+ return _this.init_1()
+ }
+ timeout(200, function() { // firefox never fires this onload
+ if (!_this.init_1_called) {
return _this.init_1()
}
- timeout(200, function() { // firefox never fires this onload
- if (!_this.init_1_called) {
- return _this.init_1()
- }
- })
- _this.outer_idoc.body.appendChild(
- domify(_this.outer_idoc, {div: {id: 'wrap1', children: [
- domify(_this.outer_idoc, {div: {
- style: "position: absolute; top: 0; left: 1px; font-size: 10px",
- children: [domify(_this.outer_idoc, {text: "Peach HTML5 Editor"})]
- }}),
- _this.wrap2 = domify(_this.outer_idoc, {div: {id: 'wrap2', children: [
- domify(_this.outer_idoc, {div: {id: 'wrap3', children: [
- _this.iframe,
- _this.overlay = domify(_this.outer_idoc, { div: { id: 'overlay' }})
- ]}})
+ })
+ _this.outer_idoc.body.appendChild(
+ domify(_this.outer_idoc, {div: {id: 'wrap1', children: [
+ domify(_this.outer_idoc, {div: {
+ style: "position: absolute; top: 0; left: 1px; font-size: 10px",
+ children: [domify(_this.outer_idoc, {text: "Peach HTML5 Editor"})]
+ }}),
+ _this.wrap2 = domify(_this.outer_idoc, {div: {id: 'wrap2', children: [
+ domify(_this.outer_idoc, {div: {id: 'wrap3', children: [
+ _this.iframe,
+ _this.overlay = domify(_this.outer_idoc, { div: { id: 'overlay' }})
]}})
]}})
- )
- }
- })(this)
+ ]}})
+ )
+ }})(this)
outer_wrap = domify(document, {div: {"class": 'peach_html5_editor' }})
this.in_el.parentNode.appendChild(outer_wrap)
outer_bounds = get_el_bounds(outer_wrap)
}
}
PeachHTML5Editor.prototype.init_2 = function() { // this.iframe and it's css file(s) are ready
- this.overlay.onclick = (function(_this) {
- return function(e) {
- _this.have_focus()
- return event_return(e, _this.onclick(e))
- }
- })(this)
- this.overlay.ondoubleclick = (function(_this) {
- return function(e) {
+ var _this = this
+ var this_event_bind = function (cb) {
+ return function (e) {
_this.have_focus()
- return event_return(e, _this.ondoubleclick(e))
+ event_return(e, _this[cb](e))
}
- })(this)
- this.outer_idoc.body.onkeyup = (function(_this) {
- return function(e) {
- _this.have_focus()
- return event_return(e, _this.onkeyup(e))
- }
- })(this)
- this.outer_idoc.body.onkeydown = (function(_this) {
- return function(e) {
- _this.have_focus()
- return event_return(e, _this.onkeydown(e))
- }
- })(this)
- this.outer_idoc.body.onkeypress = (function(_this) {
- return function(e) {
- _this.have_focus()
- return event_return(e, _this.onkeypress(e))
- }
- })(this)
+ }
+ this.overlay.onmousedown = this_event_bind('onclick')
+ this.overlay.ondoubleclick = this_event_bind('ondoubleclick')
+ this.outer_idoc.body.onkeyup = this_event_bind('onkeyup')
+ this.outer_idoc.body.onkeydown = this_event_bind('onkeydown')
+ this.outer_idoc.body.onkeypress = this_event_bind('onkeypress')
this.load_html(this.in_el.value)
if (this.options.on_init != null) {
return this.options.on_init()
ret += 'padding: 0;'
ret += 'color: black;'
ret += 'background: white;'
+ ret += 'font-family: sans;'
ret += '}'
ret += '#wrap1 {'
ret += "border: " + (occupy(1)) + "px solid black;"
this.kill_cursor()
}
PeachHTML5Editor.prototype.load_html = function(html) {
- this.tree = peach_parser(html, this.parser_opts)
+ this.tree = peach_parser.parse(html, this.parser_opts)
if (this.tree[0] == null ? true : this.tree[0].parent == null) {
- this.tree = peach_parser('<p style="white-space: pre-wrap"> </p>', this.parser_opts)
+ this.tree = peach_parser.parse('<p style="white-space: pre-wrap"> </p>', this.parser_opts)
}
this.tree_parent = this.tree[0].parent
this.tree_parent.el = this.idoc.body
}
PeachHTML5Editor.prototype.changed = function() {
this.in_el.onchange = null
- this.in_el.value = this.pretty_html(this.tree)
- // TODO make option for not pretty-printing: nodes_to_html(tree)
+ if (this.pretty_print) {
+ this.in_el.value = this.pretty_html(this.tree)
+ } else {
+ this.in_el.value = nodes_to_html(this.tree)
+ }
this.in_el.onchange = (function(_this) { return function() {
return _this.load_html(_this.in_el.value)
}})(this)
PeachHTML5Editor.prototype.update_style_from_el = function(n) {
var style
style = n.el.getAttribute('style')
- if (style != null) {
+ if (style != null && style != '') {
return n.attrs.style = style
} else {
if (n.attrs.style != null) {
if (block) {
cb(null)
}
- if (n.children.length > 0) {
+ if (n.children.length > 0 && plaintext_elements[n.name] == null) {
iterate(n.children, cb)
}
if (block) {
}
if (need_preserve) {
// do we have it already?
- ws = this.computed_style(n, 'white-space') // FIXME implement this
+ ws = this.computed_style(n, 'white-space')
if (ws_props[ws] != null ? !ws_props[ws].space : true) {
// 2nd arg is ideal target for css rule
ws = this.preserve_space(n, block)
eats_start_sp = false
} else {
if (is_space_code(n.text.charCodeAt(n.text.length - 1))) {
- ws = this.computed_style(n, 'white-space') // FIXME implement this
+ ws = this.computed_style(n, 'white-space')
if ((ref1 = ws_props[ws]) != null ? ref1.space : void 0) {
eats_start_sp = false
} else {
is_br = true
}
is_text = false
- if (n.el.currentStyle != null) {
- cs = n.el.currentStyle
- whitespace = cs['white-space']
- display = cs['display']
- position = cs['position']
- float = cs['float']
- visibility = cs['visibility']
- } else {
- cs = this.iframe.contentWindow.getComputedStyle(n.el, null)
- whitespace = cs.getPropertyValue('white-space')
- display = cs.getPropertyValue('display')
- position = cs.getPropertyValue('position')
- float = cs.getPropertyValue('float')
- visibility = cs.getPropertyValue('visibility')
- }
+ cs = this.iframe.contentWindow.getComputedStyle(n.el, null)
+ whitespace = cs.getPropertyValue('white-space')
+ display = cs.getPropertyValue('display')
+ position = cs.getPropertyValue('position')
+ float = cs.getPropertyValue('float')
+ visibility = cs.getPropertyValue('visibility')
if (n.name === 'textarea') {
inner_flags.pre_ish = true
} else {
}
break
case 'text':
- ret += enc_text(n.text)
+ if (n.parent != null ? plaintext_elements[n.parent.name] : false) {
+ ret += n.text
+ } else if (n.parent != null ? newline_eating_elements[n.parent.name] && n.text.charAt(0) === "\n" : false) {
+ ret += enc_text("\n" + n.text)
+ } else {
+ ret += enc_text(n.text)
+ }
break
case 'comment':
ret += "<!--" + n.text + "-->" // TODO encode?
return ret
}
PeachHTML5Editor.prototype.onblur = function() {
+ this.editor_is_focused = false
this.kill_cursor()
}
PeachHTML5Editor.prototype.have_focus = function() {
- this.editor_is_focused = true
- this.poll_for_blur()
-}
-PeachHTML5Editor.prototype.poll_for_blur = function() {
- if (this.poll_for_blur_timeout != null) {
- return
+ if (!this.editor_is_focused) {
+ this.input_el.focus()
+ this.editor_is_focused = true
}
- this.poll_for_blur_timeout = timeout(150, (function(_this) { return function() {
- next_frame(function() { // pause polling when browser knows we're not active/visible/etc.
- _this.poll_for_blur_timeout = null
- if (document.activeElement === _this.outer_iframe) {
- _this.poll_for_blur()
- } else {
- _this.editor_is_focused = false
- _this.onblur()
- }
- })
- }})(this))
}
window.peach_html5_editor = function() {