{"id":3289,"date":"2014-02-26T17:05:01","date_gmt":"2014-02-26T23:05:01","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=3289"},"modified":"2014-02-26T17:14:56","modified_gmt":"2014-02-26T23:14:56","slug":"bililiterange-data","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2014\/02\/26\/bililiterange-data\/","title":{"rendered":"bililiteRange data"},"content":{"rendered":"<p>There have been a lot of times I have needed some information for a <code>bililiteRange<\/code> plugin that was associated with the underlying element, rather than a specific range. For instance, in <code class=\"language-javascript\" >bililiteRange(element).text('foo')<\/code> then <code class=\"language-javascript\" >bililiteRange(element).undo()<\/code> the undo needs to know about the previous text change. jQuery has a <code class=\"language-javascript\" >data()<\/code> method that attaches an object to the element and you can add fields to that object. Actually, it only attaches an index that points to the actual object, since at least in some browsers the garbage collector had trouble with Javascript objects attached to DOM elements and you ended up with memory leaks. I'm not sure if that is <a href=\"http:\/\/blog.j15r.com\/blog\/2009\/07\/12\/Memory_Leaks_in_IE8\">still a problem<\/a>, but it's an easy enough pattern to implement so I used it.<\/p>\n<p>I didn't want to be jQuery-dependent and I wanted to be able to some more sophisticated things with my data, so I implemented my own. At its simplest, just use:<\/p>\n<pre><code class=\"language-javascript\" >var data = bililiteRange(element).data();\r\ndata.foo = 'bar';\r\nassert(bililiteRange(element).data().foo == 'bar');<\/code><\/pre>\n<p><code class=\"language-javascript\" >bililiteRange(element).data()<\/code> returns an object that you can add fields to and they will be saved across multiple calls to <code class=\"language-javascript\" >bililiteRange<\/code>.<\/p>\n<p><!--more--><\/p>\n<p>The sophisticated part is for when I want to add information for <em>every<\/em> bililiteRange. Then I have to add the field to the prototype of the data object. That uses a \"global\" bililiteRange method, <code class=\"language-javascript\" >bililiteRange.data()<\/code> to define the field:<\/p>\n<pre><code class=\"language-javascript\" >bililiteRange.data(name [,descriptor])<\/code><\/pre>\n<p>Defines <code class=\"language-javascript\" >bililiteRange(element).data()[name]<\/code> for all bililiteRanges by adding <code class=\"language-javascript\" >name<\/code> to the data prototype. Without using the <code class=\"language-javascript\" >descriptor<\/code> parameter that's pretty useless, but <code class=\"language-javascript\" >descriptor<\/code> lets you add some useful parameters. Options include:<\/p>\n<dl>\n<dt><code>value<\/code><\/dt>\n<dd>Set the default value for the field<\/dd>\n<dt><code>enumerable<\/code><\/p>\n<dd>Sets the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Object\/defineProperty\"><code class=\"language-javascript\" >enumerable<\/code> property<\/a> for the field. If <code class=\"language-javascript\" >false<\/code>, then <code class=\"language-javascript\" >for (key in bililiteRange(element).data())<\/code> will not include that property, and <code class=\"language-javascript\" >JSON.stringify(bililiteRange(element).data())<\/code> will not list it. Defaults to <code class=\"language-javascript\" >true<\/code>.<\/dd>\n<dt><code>monitored<\/code><\/dt>\n<dd>Makes the field \"monitored\", meaning that whenever it is set, an event is triggered:<\/p>\n<pre><code class=\"language-javascript\" >bililiteRange(element).dispatch({\r\n  type: 'bililiteRangeData',\r\n  bubbles: true,\r\n  detail: {name: name, value: value}\r\n})<\/code><\/pre>\n<\/dd>\n<\/dl>\n<p>The following fields are reserved (in addition to anything in <code class=\"language-javascript\" >Object.prototype<\/code>: <code class=\"language-javascript\" >values<\/code> is an object that actually stores the values (since internally I use <code class=\"language-javascript\" >set\/get<\/code>), <code class=\"language-javascript\" >sourceRange<\/code> is the range from which the data object was originally defined, <code class=\"language-javascript\" >toJSON<\/code> is the method for making <code class=\"language-javascript\" >JSON.stringify<\/code> work, and <code class=\"language-javascript\" >all<\/code> returns an object that represents all enumerable values, not just ones set for this data object.<\/p>\n<p>The data object has a custom <code class=\"language-javascript\" >toJSON<\/code> method, so <code class=\"language-javascript\" >JSON.stringify(bililiteRange(element).data())<\/code> produces the expected results, even though the fields are not simple values. This will only display the fields that have been explicitly set, not the ones that are defined on the prototype. To get all the enumerable fields, use <code class=\"language-javascript\" >bililiteRange(element).data().all<\/code> (either in <code class=\"language-javascript\" >for (...in)<\/code> or <code class=\"language-javascript\" >JSON.stringify<\/code>).<\/p>\n<p>Internet Explorer 8 does not allow setting custom properties on plain Javascript elements, so the <code class=\"language-javascript\" >enumerable<\/code> and <code class=\"language-javascript\" >monitored<\/code> fields are ignored, and <code class=\"language-javascript\" >all<\/code> is always <code class=\"language-javascript\" >undefined<\/code>.<\/p>\n<h3>Examples<\/h3>\n<p>The <a href=\"http:\/\/bililite.com\/blog\/2013\/02\/08\/bililiterange-plugins\/\" title=\"bililiteRange Plugins\">bililiteRange utilities<\/a> redefine <code class=\"language-javascript\" >text()<\/code> to allow \"autoindenting\" with a third parameter. We can implement this in an editable element using the <code class=\"language-javascript\" >data<\/code> to hold the option:<\/p>\n<pre><code class=\"language-javascript\" >\r\nbililiteRange.data ('autoindent', {value: false}); \/\/ default\r\nvar rng = bililiteRange(editor);\r\ndocument.querySelector('#autoindentCheckbox').addEventListener('change', function() { rng.data().autoindent = this.checked });\r\nrng.listen('keydown', function(evt){\r\n\tif (evt.keyCode == 13){\r\n\t\trng.bounds('selection').text('\\n','end', rng.data().autoindent).select();\r\n\t\tevt.preventDefault();\r\n\t}\r\n});\r\n<\/code><\/pre>\n<p>And <a href=\"http:\/\/bililite.com\/blog\/2013\/12\/16\/simple-syntax-highlighting-editor-with-prism\/\" title=\"Simple syntax-highlighting editor with Prism\"><code>fancyText<\/code><\/a> does exactly that. <a href=\"\/blog\/blogfiles\/prism\/prismeditor.html\">See the demo<\/a>.<\/p>\n<p><a href=\"http:\/\/caniuse.com\/css3-tabsize\"><code>tab-size<\/code><\/a> is inconsistently implemented, but we could use a data property to monitor for changes and set the style based on that:<\/p>\n<pre><code class=\"language-javascript\" >\r\nbililiteRange.data ('tabSize', {monitored: true});\r\nvar rng = bililiteRange(editor);\r\nrng.listen('bililiteRangeData', function(evt){\r\n\tif (evt.detail && evt.detail.name == 'tabSize') {\r\n\t\teditor.style.tabSize =\r\n\t\teditor.style.mozTabSize = evt.detail.value;\r\n\t}\r\n});\r\nrng.data().tabSize = 4;\r\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>There have been a lot of times I have needed some information for a bililiteRange plugin that was associated with the underlying element, rather than a specific range. For instance, in bililiteRange(element).text('foo') then bililiteRange(element).undo() the undo needs to know about the previous text change. jQuery has a data() method that attaches an object to the [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,10],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3289"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=3289"}],"version-history":[{"count":9,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3289\/revisions"}],"predecessor-version":[{"id":3299,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3289\/revisions\/3299"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=3289"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=3289"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=3289"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}