source: Dev/trunk/src/client/dojox/gfx/_gfxBidiSupport.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 13.2 KB
Line 
1define(["./_base", "dojo/_base/lang","dojo/_base/sniff", "dojo/dom", "dojo/_base/html", "dojo/_base/array",
2                "./utils", "./shape", "./path", "dojox/string/BidiEngine"],
3function(g, lang, has, dom, html, arr, utils, shapeLib, pathLib, BidiEngine){
4        lang.getObject("dojox.gfx._gfxBidiSupport", true);
5
6        /*=====
7        // Prevent changes here from masking the definitions in _base.js from the doc parser
8        var origG = g;
9        g = {};
10        =====*/
11
12        switch (g.renderer){
13                case 'vml':
14                        g.isVml = true;
15                        break;
16                case 'svg':
17                        g.isSvg = true;
18                        if(g.svg.useSvgWeb){
19                                g.isSvgWeb = true;
20                        }
21                        break;
22                case 'silverlight':
23                        g.isSilverlight = true;
24                        break;
25                case 'canvas':
26                case 'canvasWithEvents':
27                        g.isCanvas = true;
28                        break;
29        }
30
31        var bidi_const = {
32                LRM : '\u200E',
33                LRE : '\u202A',
34                PDF : '\u202C',
35                RLM : '\u200f',
36                RLE : '\u202B'
37        };
38
39        /*===== g = origG; =====*/
40
41        // the object that performs text transformations.
42        var bidiEngine = new BidiEngine();
43
44        lang.extend(g.shape.Surface, {
45                // textDir: String
46                //              Will be used as default for Text/TextPath/Group objects that created by this surface
47                //              and textDir wasn't directly specified for them, though the bidi support was loaded.
48                //              Can be set in two ways:
49                //
50                //              1. When the surface is created and textDir value passed to it as fourth
51                //              parameter.
52                //              2. Using the setTextDir(String) function, when this function is used the value
53                //              of textDir propagates to all of it's children and the children of children (for Groups) etc.
54                textDir: "",
55
56                setTextDir: function(/*String*/newTextDir){
57                        // summary:
58                        //              Used for propagation and change of textDir.
59                        //              newTextDir will be forced as textDir for all of it's children (Group/Text/TextPath).
60                        setTextDir(this, newTextDir);
61                },
62
63                getTextDir: function(){
64                        return this.textDir;
65                }
66        });
67
68        lang.extend(g.Group, {                         
69                // textDir: String
70                //              Will be used for inheritance, or as default for text objects
71                //              that textDir wasn't directly specified for them but the bidi support was required.
72                textDir: "",
73
74                setTextDir: function(/*String*/newTextDir){
75                        // summary:
76                        //              Used for propagation and change of textDir.
77                        //              newTextDir will be forced as textDir for all of it's children (Group/Text/TextPath).
78                        setTextDir(this, newTextDir);
79                },
80
81                getTextDir: function(){
82                        return this.textDir;
83                }       
84        });
85       
86        lang.extend(g.Text, { 
87                // summary:
88                //              Overrides some of dojox/gfx.Text properties, and adds some
89                //              for bidi support.
90               
91                // textDir: String
92                //              Used for displaying bidi scripts in right layout.
93                //              Defines the base direction of text that displayed, can have 3 values:
94                //
95                //              1. "ltr" - base direction is left to right.
96                //              2. "rtl" - base direction is right to left.
97                //              3. "auto" - base direction is contextual (defined by first strong character).
98                textDir: "",
99
100                formatText: function (/*String*/ text, /*String*/ textDir){
101                        // summary:
102                        //              Applies the right transform on text, according to renderer.
103                        // text:       
104                        //              the string for manipulation, by default return value.
105                        // textDir:     
106                        //              Text direction.
107                        //              Can be:
108                        //
109                        //              1. "ltr" - for left to right layout.
110                        //              2. "rtl" - for right to left layout
111                        //              3. "auto" - for contextual layout: the first strong letter decides the direction.
112                        // description:
113                        //              Finds the right transformation that should be applied on the text, according to renderer.
114                        //              Was tested in:
115                        //
116                        //              Renderers (browser for testing):
117                        //
118                        //              - canvas (FF, Chrome, Safari),
119                        //              - vml (IE),
120                        //              - svg (FF, Chrome, Safari, Opera),
121                        //              - silverlight (IE, Chrome, Safari, Opera),
122                        //              - svgWeb(FF, Chrome, Safari, Opera, IE).
123                        //
124                        //              Browsers [browser version that was tested]:
125                        //
126                        //              - IE [6,7,8], FF [3.6],
127                        //              - Chrome (latest for March 2011),
128                        //              - Safari [5.0.3],
129                        //              - Opera [11.01].
130
131                        if(textDir && text && text.length > 1){
132                                var sourceDir = "ltr", targetDir = textDir;
133       
134                                if(targetDir == "auto"){
135                                        //is auto by default
136                                        if(g.isVml){
137                                                return text;
138                                        }
139                                        targetDir = bidiEngine.checkContextual(text);
140                                }
141       
142                                if(g.isVml){
143                                        sourceDir = bidiEngine.checkContextual(text);
144                                        if(targetDir != sourceDir){
145                                                if(targetDir == "rtl"){
146                                                        return !bidiEngine.hasBidiChar(text) ? bidiEngine.bidiTransform(text,"IRNNN","ILNNN") : bidi_const.RLM + bidi_const.RLM + text;
147                                                }else{
148                                                        return bidi_const.LRM + text;
149                                                }
150                                        }
151                                        return text;
152                                }
153       
154                                if(g.isSvgWeb){
155                                        if(targetDir == "rtl"){
156                                                return bidiEngine.bidiTransform(text,"IRNNN","ILNNN");
157                                        }
158                                        return text;
159                                }
160       
161                                if(g.isSilverlight){
162                                        return (targetDir == "rtl") ? bidiEngine.bidiTransform(text,"IRNNN","VLYNN") : bidiEngine.bidiTransform(text,"ILNNN","VLYNN");
163                                }
164       
165                                if(g.isCanvas){
166                                        return (targetDir == "rtl") ? bidi_const.RLE + text + bidi_const.PDF : bidi_const.LRE + text + bidi_const.PDF;
167                                }
168       
169                                if(g.isSvg){
170                                        if(has("ff") < 4){
171                                                return (targetDir == "rtl") ? bidiEngine.bidiTransform(text,"IRYNN","VLNNN") : bidiEngine.bidiTransform(text,"ILYNN","VLNNN");
172                                        }else{
173                                                return bidi_const.LRM + (targetDir == "rtl" ? bidi_const.RLE : bidi_const.LRE) + text + bidi_const.PDF;
174                                        }                                       
175                                }                                       
176                        }
177                        return text;
178                },     
179
180                bidiPreprocess: function(newShape){     
181                        return newShape;
182                }
183        });
184
185        lang.extend(g.TextPath, {
186                // textDir: String
187                //              Used for displaying bidi scripts in right layout.
188                //              Defines the base direction of text that displayed, can have 3 values:
189                //
190                //              1. "ltr" - base direction is left to right.
191                //              2. "rtl" - base direction is right to left.
192                //              3. "auto" - base direction is contextual (defined by first strong character).
193                textDir: "",
194
195                formatText: function (/*String*/text, /*String*/textDir){
196                        // summary:
197                        //              Applies the right transform on text, according to renderer.
198                        // text:
199                        //              the string for manipulation, by default return value.
200                        // textDir:
201                        //              text direction direction.
202                        //              Can be:
203                        //
204                        //              1. "ltr" - for left to right layout.
205                        //              2. "rtl" - for right to left layout
206                        //              3. "auto" - for contextual layout: the first strong letter decides the direction.
207                        // description:
208                        //              Finds the right transformation that should be applied on the text, according to renderer.
209                        //              Was tested in:
210                        //
211                        //              Renderers:
212                        //              canvas (FF, Chrome, Safari), vml (IE), svg (FF, Chrome, Safari, Opera), silverlight (IE8), svgWeb(FF, Chrome, Safari, Opera, IE).
213                        //
214                        //              Browsers:
215                        //              IE [6,7,8], FF [3.6], Chrome (latest for February 2011), Safari [5.0.3], Opera [11.01].
216
217                        if(textDir && text && text.length > 1){
218                                var sourceDir = "ltr", targetDir = textDir;
219
220                                if(targetDir == "auto"){
221                                        //is auto by default
222                                        if(g.isVml){
223                                                return text;
224                                        }
225                                        targetDir = bidiEngine.checkContextual(text);
226                                }
227
228                                if(g.isVml){
229                                        sourceDir = bidiEngine.checkContextual(text);
230                                        if(targetDir != sourceDir){
231                                                if(targetDir == "rtl"){
232                                                        return !bidiEngine.hasBidiChar(text) ? bidiEngine.bidiTransform(text,"IRNNN","ILNNN") : bidi_const.RLM + bidi_const.RLM + text;
233                                                }else{
234                                                        return bidi_const.LRM + text;
235                                                }
236                                        }
237                                        return text;
238                                }
239                                if(g.isSvgWeb){
240                                        if(targetDir == "rtl"){
241                                                return bidiEngine.bidiTransform(text,"IRNNN","ILNNN");
242                                        }
243                                        return text;
244                                }
245                                //unlike the g.Text that is rendered in logical layout for Bidi scripts.
246                                //for g.TextPath in svg always visual -> bidi script is unreadable (except Opera and FF start from version 4)
247                                if(g.isSvg){
248                                        if(has("opera") || has("ff") >= 4){
249                                                text = bidi_const.LRM + (targetDir == "rtl"? bidi_const.RLE : bidi_const.LRE) + text + bidi_const.PDF;
250                                        }else{
251                                                text = (targetDir == "rtl") ? bidiEngine.bidiTransform(text,"IRYNN","VLNNN") : bidiEngine.bidiTransform(text,"ILYNN","VLNNN");
252                                        }
253                                }                                       
254                        }       
255                        return text;
256                },
257                bidiPreprocess: function(newText){
258                        if(newText && (typeof newText == "string")){
259                                this.origText = newText;
260                                newText = this.formatText(newText,this.textDir);
261                        }
262                        return newText;
263                }
264        });     
265               
266        var extendMethod = function(shape, method, before, after){
267                // summary:
268                //              Some helper function. Used for extending methods of shape.
269                // shape: Object
270                //              The shape we overriding it's method.
271                // method: String
272                //              The method that is extended, the original method is called before or after
273                //              functions that passed to extendMethod.
274                // before: function
275                //              If defined this function will be executed before the original method.
276                // after: function
277                //              If defined this function will be executed after the original method.
278                var old = shape.prototype[method];
279                shape.prototype[method] =
280                        function(){
281                                var rBefore;
282                                if (before){
283                                        rBefore = before.apply(this, arguments);
284                                }
285                                var r = old.call(this, rBefore);
286                                if (after){
287                                        r = after.call(this, r, arguments);
288                                }
289                                return r;
290                        };
291        };
292
293        var bidiPreprocess = function(newText){
294                if (newText){ 
295                        if (newText.textDir){
296                                newText.textDir = validateTextDir(newText.textDir);
297                        }
298                        if (newText.text && (newText.text instanceof Array)){
299                                newText.text = newText.text.join(",");
300                        }
301                }
302                if(newText && (newText.text != undefined || newText.textDir) && (this.textDir != newText.textDir || newText.text != this.origText)){
303                        // store the original text.
304                        this.origText = (newText.text != undefined) ? newText.text : this.origText;
305                        if(newText.textDir){
306                                this.textDir = newText.textDir;
307                        }
308                        newText.text = this.formatText(this.origText,this.textDir);
309                }
310                return this.bidiPreprocess(newText);
311
312        };
313
314        // Instead of adding bidiPreprocess to all renders one by one
315        // use the extendMethod, at first there's a need for bidi transformation
316        // on text then call to original setShape.
317        extendMethod(g.Text,"setShape", bidiPreprocess, null);
318        extendMethod(g.TextPath,"setText", bidiPreprocess, null);
319       
320        var restoreText = function(origObj){
321                var obj = lang.clone(origObj);
322                if (obj && this.origText){
323                        obj.text = this.origText;
324                }
325                return obj;
326        };
327
328        // Instead of adding restoreText to all renders one by one
329        // use the extendMethod, at first get the shape by calling the original getShape,
330        // than resrore original text (without the text transformations).
331        extendMethod(g.Text, "getShape", null, restoreText);
332        extendMethod(g.TextPath, "getText", null, restoreText);
333
334        var groupTextDir = function(group, args){
335                var textDir;
336                if (args && args[0]){
337                        textDir = validateTextDir(args[0]);
338                }
339                group.setTextDir(textDir ? textDir : this.textDir);
340                return group;   // dojox/gfx.Group
341        };
342
343        // In creation of Group there's a need to update it's textDir,
344        // so instead of doing it in renders one by one (vml vs others)
345        // use the extendMethod, at first the original createGroup is applied, the
346        // groupTextDir which is setts Group's textDir as it's father's or if was defined
347        // by user by this value.
348        extendMethod(g.Surface, "createGroup", null, groupTextDir);
349        extendMethod(g.Group, "createGroup", null, groupTextDir);
350
351        var textDirPreprocess =  function(text){
352                // inherit from surface / group  if textDir is defined there
353                if(text){
354                        var textDir = text.textDir ? validateTextDir(text.textDir) : this.textDir;
355                        if(textDir){
356                                text.textDir = textDir;
357                        }
358                }
359                return text;
360        };
361
362        // In creation there's a need to some preprocess,
363        // so instead of doing it in renders one by one (vml vs others)
364        // use the extendMethod, at first the textDirPreprocess function handles the input
365        // then the original createXXXXXX is applied.
366        extendMethod(g.Surface,"createText", textDirPreprocess, null);
367        extendMethod(g.Surface,"createTextPath", textDirPreprocess, null);
368        extendMethod(g.Group,"createText", textDirPreprocess, null);
369        extendMethod(g.Group,"createTextPath", textDirPreprocess, null);
370
371        /*=====
372        // don't mask definition of original createSurface() function from doc parser
373        g = {};
374        =====*/
375
376        g.createSurface = function(parentNode, width, height, textDir) {
377                var s = g[g.renderer].createSurface(parentNode, width, height);
378                var tDir = validateTextDir(textDir);
379               
380                if(g.isSvgWeb){
381                        s.textDir = tDir ? tDir : html.style(dom.byId(parentNode),"direction");
382                        return s;
383                }
384                // if textDir was defined use it, else get default value.
385                //s.textDir = tDir ? tDir : html.style(s.rawNode,"direction");
386                if(g.isVml || g.isSvg || g.isCanvas){
387                        s.textDir = tDir ? tDir : html.style(s.rawNode,"direction");
388                }
389                if(g.isSilverlight){
390                        // allow this once rawNode will be able for the silverlight
391                        //s.textDir = tDir ? tDir : dojo.style(s.rawNode,"direction");
392                        s.textDir = tDir ? tDir : html.style(s._nodes[1],"direction");
393                }
394               
395                return s;
396        };
397        /*===== g = origG; =====*/
398
399        // some helper functions
400       
401        function setTextDir(/*Object*/ obj, /*String*/ newTextDir){
402                var tDir = validateTextDir(newTextDir);
403                if (tDir){
404                        g.utils.forEach(obj,function(e){
405                                if(e instanceof g.Surface || e instanceof g.Group){
406                                        e.textDir = tDir;
407                                }               
408                                if(e instanceof g.Text){
409                                        e.setShape({textDir: tDir});
410                                }
411                                if(e instanceof g.TextPath){
412                                        e.setText({textDir: tDir})
413                                }
414                        }, obj);
415                }
416                return obj;
417        }
418
419        function validateTextDir(textDir){
420                var validValues = ["ltr","rtl","auto"];
421                if (textDir){
422                        textDir = textDir.toLowerCase();
423                        if (arr.indexOf(validValues, textDir) < 0){
424                                return null;
425                        }
426                }
427                return textDir;
428        }
429
430        return g; // return gfx api augmented with bidi support
431});
432
Note: See TracBrowser for help on using the repository browser.