1 | define(["dojo/_base/lang", "dojo/_base/html", "dojo/_base/array", "dojo/_base/sniff", |
---|
2 | "dojo/dom","dojo/dom-construct", |
---|
3 | "dojox/gfx", "dojox/gfx/_gfxBidiSupport", "./Chart", "./axis2d/common", "dojox/string/BidiEngine", "dojox/lang/functional"], |
---|
4 | function(lang, html, arr, has, dom, domConstruct, g, gBidi, Chart, da, BidiEngine, df){ |
---|
5 | |
---|
6 | var bidiEngine = new BidiEngine(); |
---|
7 | |
---|
8 | lang.extend(Chart, { |
---|
9 | // summary: |
---|
10 | // Add support for bidi scripts. |
---|
11 | // description: |
---|
12 | // Bidi stands for support for languages with a bidirectional script. |
---|
13 | // There's a special need for displaying BIDI text in rtl direction |
---|
14 | // in ltr GUI, sometimes needed auto support. |
---|
15 | // dojox.charting does not support control over base text direction provided in Dojo. |
---|
16 | |
---|
17 | // textDir: String |
---|
18 | // Bi-directional support, the main variable which is responsible for the direction of the text. |
---|
19 | // The text direction can be different than the GUI direction by using this parameter. |
---|
20 | // Allowed values: |
---|
21 | // 1. "ltr" |
---|
22 | // 2. "rtl" |
---|
23 | // 3. "auto" - contextual the direction of a text defined by first strong letter. |
---|
24 | // By default is as the page direction. |
---|
25 | textDir:"", |
---|
26 | |
---|
27 | getTextDir: function(/*String*/text){ |
---|
28 | // summary: |
---|
29 | // Return direction of the text. |
---|
30 | // description: |
---|
31 | // If textDir is ltr or rtl returns the value. |
---|
32 | // If it's auto, calls to another function that responsible |
---|
33 | // for checking the value, and defining the direction. |
---|
34 | // text: |
---|
35 | // Used in case textDir is "auto", this case the direction is according to the first |
---|
36 | // strong (directionally - which direction is strong defined) letter. |
---|
37 | // tags: |
---|
38 | // protected. |
---|
39 | var textDir = this.textDir == "auto" ? bidiEngine.checkContextual(text) : this.textDir; |
---|
40 | // providing default value |
---|
41 | if(!textDir){ |
---|
42 | textDir = html.style(this.node,"direction"); |
---|
43 | } |
---|
44 | return textDir; |
---|
45 | }, |
---|
46 | |
---|
47 | postscript: function(node,args){ |
---|
48 | // summary: |
---|
49 | // Kicks off chart instantiation. |
---|
50 | // description: |
---|
51 | // Used for setting the textDir of the chart. |
---|
52 | // tags: |
---|
53 | // private |
---|
54 | |
---|
55 | // validate textDir |
---|
56 | var textDir = args ? (args["textDir"] ? validateTextDir(args["textDir"]) : "") : ""; |
---|
57 | // if textDir wasn't defined or was defined wrong, apply default value |
---|
58 | textDir = textDir ? textDir : html.style(this.node,"direction"); |
---|
59 | this.textDir = textDir; |
---|
60 | |
---|
61 | this.surface.textDir = textDir; |
---|
62 | |
---|
63 | // two data structures, used for storing data for further enablement to change |
---|
64 | // textDir dynamically |
---|
65 | this.htmlElementsRegistry = []; |
---|
66 | this.truncatedLabelsRegistry = []; |
---|
67 | }, |
---|
68 | |
---|
69 | setTextDir: function(/*String*/ newTextDir, obj){ |
---|
70 | // summary: |
---|
71 | // Setter for the textDir attribute. |
---|
72 | // description: |
---|
73 | // Allows dynamically set the textDir, goes over all the text-children and |
---|
74 | // updates their base text direction. |
---|
75 | // tags: |
---|
76 | // public |
---|
77 | |
---|
78 | if(newTextDir == this.textDir){ |
---|
79 | return this; |
---|
80 | } |
---|
81 | if(validateTextDir(newTextDir) != null){ |
---|
82 | this.textDir = newTextDir; |
---|
83 | |
---|
84 | // set automatically all the gfx objects that were created by this surface |
---|
85 | // (groups, text objects) |
---|
86 | this.surface.setTextDir(newTextDir); |
---|
87 | |
---|
88 | // truncated labels that were created with gfx creator need to recalculate dir |
---|
89 | // for case like: "111111A" (A stands for bidi character) and the truncation |
---|
90 | // is "111..." If the textDir is auto, the display should be: "...111" but in gfx |
---|
91 | // case we will get "111...". Because this.surface.setTextDir will calculate the dir of truncated |
---|
92 | // label, which value is "111..." but th real is "111111A". |
---|
93 | // each time we created a gfx truncated label we stored it in the truncatedLabelsRegistry, so update now |
---|
94 | // the registry. |
---|
95 | if(this.truncatedLabelsRegistry && newTextDir == "auto"){ |
---|
96 | arr.forEach(this.truncatedLabelsRegistry, function(elem){ |
---|
97 | var tDir = this.getTextDir(elem["label"]); |
---|
98 | if(elem["element"].textDir != tDir){ |
---|
99 | elem["element"].setShape({textDir: tDir}); |
---|
100 | } |
---|
101 | }, this); |
---|
102 | } |
---|
103 | |
---|
104 | // re-render axes with html labels. for recalculation of the labels |
---|
105 | // positions etc. |
---|
106 | // create array of keys for all the axis in chart |
---|
107 | var axesKeyArr = df.keys(this.axes); |
---|
108 | if(axesKeyArr.length > 0){ |
---|
109 | // iterate over the axes, and for each that have html labels render it. |
---|
110 | arr.forEach(axesKeyArr, function(key, index, arr){ |
---|
111 | // get the axis |
---|
112 | var axis = this.axes[key]; |
---|
113 | // if the axis has html labels |
---|
114 | if(axis.htmlElements[0]){ |
---|
115 | axis.dirty = true; |
---|
116 | axis.render(this.dim, this.offsets); |
---|
117 | } |
---|
118 | },this); |
---|
119 | |
---|
120 | // recreate title |
---|
121 | if(this.title){ |
---|
122 | var forceHtmlLabels = (g.renderer == "canvas"), |
---|
123 | labelType = forceHtmlLabels || !has("ie") && !has("opera") ? "html" : "gfx", |
---|
124 | tsize = g.normalizedLength(g.splitFontString(this.titleFont).size); |
---|
125 | // remove the title |
---|
126 | domConstruct.destroy(this.chartTitle); |
---|
127 | this.chartTitle =null; |
---|
128 | // create the new title |
---|
129 | this.chartTitle = da.createText[labelType]( |
---|
130 | this, |
---|
131 | this.surface, |
---|
132 | this.dim.width/2, |
---|
133 | this.titlePos=="top" ? tsize + this.margins.t : this.dim.height - this.margins.b, |
---|
134 | "middle", |
---|
135 | this.title, |
---|
136 | this.titleFont, |
---|
137 | this.titleFontColor |
---|
138 | ); |
---|
139 | } |
---|
140 | }else{ |
---|
141 | // case of pies, spiders etc. |
---|
142 | arr.forEach(this.htmlElementsRegistry, function(elem, index, arr){ |
---|
143 | var tDir = newTextDir == "auto" ? this.getTextDir(elem[4]) : newTextDir; |
---|
144 | if(elem[0].children[0] && elem[0].children[0].dir != tDir){ |
---|
145 | dom.destroy(elem[0].children[0]); |
---|
146 | elem[0].children[0] = da.createText["html"] |
---|
147 | (this, this.surface, elem[1], elem[2], elem[3], elem[4], elem[5], elem[6]).children[0]; |
---|
148 | } |
---|
149 | },this); |
---|
150 | } |
---|
151 | } |
---|
152 | }, |
---|
153 | |
---|
154 | truncateBidi: function(elem, label, labelType){ |
---|
155 | // summary: |
---|
156 | // Enables bidi support for truncated labels. |
---|
157 | // description: |
---|
158 | // Can be two types of labels: html or gfx. |
---|
159 | // gfx labels: |
---|
160 | // Need to be stored in registry to be used when the textDir will be set dynamically. |
---|
161 | // Additional work on truncated labels is needed for case as 111111A (A stands for "bidi" character rtl directioned). |
---|
162 | // let say in this case the truncation is "111..." If the textDir is auto, the display should be: "...111" but in gfx |
---|
163 | // case we will get "111...". Because this.surface.setTextDir will calculate the dir of truncated |
---|
164 | // label, which value is "111..." but th real is "111111A". |
---|
165 | // each time we created a gfx truncated label we store it in the truncatedLabelsRegistry. |
---|
166 | // html labels: |
---|
167 | // no need for repository (stored in another place). Here we only need to update the current dir according to textDir. |
---|
168 | // tags: |
---|
169 | // private |
---|
170 | |
---|
171 | if(labelType == "gfx"){ |
---|
172 | // store truncated gfx labels in the data structure. |
---|
173 | this.truncatedLabelsRegistry.push({element: elem, label: label}); |
---|
174 | if(this.textDir == "auto"){ |
---|
175 | elem.setShape({textDir: this.getTextDir(label)}); |
---|
176 | } |
---|
177 | } |
---|
178 | if(labelType == "html" && this.textDir == "auto"){ |
---|
179 | elem.children[0].dir = this.getTextDir(label); |
---|
180 | } |
---|
181 | } |
---|
182 | }); |
---|
183 | |
---|
184 | var extendMethod = function(obj, method, bundleByPrototype, before, after){ |
---|
185 | // Some helper function. Used for extending method of obj. |
---|
186 | // obj: Object |
---|
187 | // The obj we overriding it's method. |
---|
188 | // method: String |
---|
189 | // The method that is extended, the original method is called before or after |
---|
190 | // functions that passed to extendMethod. |
---|
191 | // bundleByPrototype: boolean |
---|
192 | // There's two methods to extend, using prototype or not. |
---|
193 | // before: function |
---|
194 | // If defined this function will be executed before the original method. |
---|
195 | // after: function |
---|
196 | // If defined this function will be executed after the original method. |
---|
197 | if(bundleByPrototype){ |
---|
198 | var old = obj.prototype[method]; |
---|
199 | obj.prototype[method] = |
---|
200 | function(){ |
---|
201 | var rBefore; |
---|
202 | if (before){ |
---|
203 | rBefore = before.apply(this, arguments); |
---|
204 | } |
---|
205 | var r = old.apply(this, rBefore); |
---|
206 | if (after){ |
---|
207 | r = after.call(this, r, arguments); |
---|
208 | } |
---|
209 | return r; |
---|
210 | }; |
---|
211 | }else{ |
---|
212 | var old = lang.clone(obj[method]); |
---|
213 | obj[method] = |
---|
214 | function(){ |
---|
215 | var rBefore; |
---|
216 | if (before){ |
---|
217 | rBefore = before.apply(this, arguments); |
---|
218 | } |
---|
219 | var r = old.apply(this, arguments); |
---|
220 | if (after){ |
---|
221 | after(r, arguments); |
---|
222 | } |
---|
223 | return r; |
---|
224 | }; |
---|
225 | } |
---|
226 | }; |
---|
227 | |
---|
228 | var labelPreprocess = function(elem, chart, label, truncatedLabel, font, elemType){ |
---|
229 | // aditional preprocessing of the labels, needed for rtl base text direction in LTR |
---|
230 | // GUI, or for ltr base text direction for RTL GUI. |
---|
231 | |
---|
232 | var isChartDirectionRtl = (html.style(chart.node,"direction") == "rtl"); |
---|
233 | var isBaseTextDirRtl = (chart.getTextDir(label) == "rtl"); |
---|
234 | |
---|
235 | if(isBaseTextDirRtl && !isChartDirectionRtl){ |
---|
236 | label = "<span dir='rtl'>" + label +"</span>"; |
---|
237 | } |
---|
238 | if(!isBaseTextDirRtl && isChartDirectionRtl){ |
---|
239 | label = "<span dir='ltr'>" + label +"</span>"; |
---|
240 | } |
---|
241 | |
---|
242 | return arguments; |
---|
243 | }; |
---|
244 | |
---|
245 | // connect labelPreprocess to run before labelTooltip. |
---|
246 | // patch it only is available |
---|
247 | if(dojox.charting.axis2d && dojox.charting.axis2d.Default){ |
---|
248 | extendMethod(dojox.charting.axis2d.Default,"labelTooltip",true, labelPreprocess, null); |
---|
249 | //extendMethod(dijit,"showTooltip",false, labelPreprocess, null); |
---|
250 | } |
---|
251 | |
---|
252 | function htmlCreateText(r, agumentsArr){ |
---|
253 | // function to register HTML elements that created by html.createText, this array |
---|
254 | // needed for allowing to change textDir dynamically. |
---|
255 | agumentsArr[0].htmlElementsRegistry.push([r, agumentsArr[2], agumentsArr[3], agumentsArr[4], agumentsArr[5], agumentsArr[6], agumentsArr[7]]); |
---|
256 | } |
---|
257 | |
---|
258 | extendMethod(da.createText,"html", false, null, htmlCreateText); |
---|
259 | |
---|
260 | function validateTextDir(textDir){ |
---|
261 | return /^(ltr|rtl|auto)$/.test(textDir) ? textDir : null; |
---|
262 | } |
---|
263 | |
---|
264 | }); |
---|