OSDN Git Service

fee049105460c143241a20014a4fea2ca0b55c6e
[pettanr/pettanr.git] / app / assets / javascripts / peta-common.js
1 /*\r
2  * pettanR peta.common.js\r
3  *   version 0.5.56\r
4  * \r
5  *   author:\r
6  *     itozyun\r
7  *   licence:\r
8  *     3-clause BSD\r
9  */\r
10 \r
11 var pettanr = ( function(){\r
12         var     IS_LOCAL = document.location.href.indexOf( 'file:') === 0,\r
13                 URL_PARAMS = ( function(){\r
14                         var search = document.location.search,\r
15                                 l = search.length;\r
16                     if( 1 < l){\r
17                         var     query = search.substring( 1),\r
18                                         params = query.split( '&'),\r
19                                         ret = {}, elm, name, v;\r
20                         while( params.length > 0){\r
21                             elm = params.shift().split( '=');\r
22                                         name = decodeURIComponent( elm[ 0 ]);\r
23                                         if( elm.length === 2){\r
24                                                 v = decodeURIComponent( elm[ 1]);\r
25                                                 if( '' + parseFloat( v ) === v ) v = parseFloat( v );\r
26                                                 if( '' + parseInt( v, 10 ) === '0' + v ) v = parseInt( v, 10 );\r
27                                                 if( v === 'true' ) v = true;\r
28                                                 if( v === 'false' ) v = false;\r
29                                                 if( v === 'null' ) v = null;\r
30                                                 if( v === 'undefined' ) v = undefined;\r
31                                     ret[ name] = v;\r
32                                         } else\r
33                                         if( elm.length === 1){\r
34                                                 ret[ name] = true;\r
35                                         };\r
36                                 };\r
37                         return ret;\r
38                     };\r
39                     return {};\r
40                 })(),\r
41                 IS_DEBUG = Type.isBoolean( URL_PARAMS.debug ) ? URL_PARAMS.debug : IS_LOCAL === true;\r
42         return {\r
43                 version: '0.5.40',\r
44                 URL_PARAMS: URL_PARAMS,\r
45                 LOCAL: IS_LOCAL,\r
46                 DEBUG: IS_DEBUG,\r
47                 LINE_FEED_CODE_TEXTAREA: ( function(){\r
48                         var text = document.createElement('textarea');\r
49                         text.value = '\n';\r
50                         return text.value;\r
51                 })(),\r
52                 LINE_FEED_CODE_PRE: ( function(){\r
53                         var pre = document.createElement('pre');\r
54                         pre.appendChild( document.createTextNode('\n'));\r
55                         return pre.firstChild.data;\r
56                 })()\r
57         }\r
58 })();\r
59 \r
60 pettanr.CONST = ( function(){\r
61         var SERVER_SUPPORT    = !( 'has_server_support' in window && has_server_support === false ),\r
62                 PETTANR_ROOT_PATH = ( function(){\r
63                         if( SERVER_SUPPORT === false ){\r
64                                 var h1 = document.getElementsByTagName( 'h1' )[ 0 ];\r
65                                 if( h1 ){\r
66                                         var a = h1.getElementsByTagName( 'a' )[ 0 ];\r
67                                         return a ? a.href : '';\r
68                                 }\r
69                                 return '';\r
70                         };\r
71                         var loc = document.location;\r
72                         return [ loc.protocol, '\/\/', loc.host, '\/' ].join( '' );\r
73                 })(),\r
74                 RELATIVE = ( function(){\r
75                         if( PETTANR_ROOT_PATH === '' ) return '';\r
76                         var ret  = '',\r
77                                 loc  = document.location,\r
78                                 path = [ loc.protocol, '\/', loc.host, '\/', loc.pathname.split( '\\' ).join( '\/' ) ].join( '' ),\r
79                                 l    = path.split( '\/' ).length - PETTANR_ROOT_PATH.split( '\/' ).length;\r
80                         for( var i=0; i<l; ++i ){\r
81                                 ret += '..\/';\r
82                         };\r
83                         return ret;\r
84                 })();\r
85                 \r
86         return {\r
87                 PETTANR_ROOT_PATH             : PETTANR_ROOT_PATH,\r
88                 URL_MY_ORIGINAL_PICTURES_JSON : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'original_pictures.json',\r
89                 URL_MY_RESOURCE_PICTURES_JSON : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/resource_pictures.json',\r
90                 URL_RESOURCE_PICTURES_JSON    : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'resource_pictures.json',\r
91                 URL_MY_RESOURCE_PICTURES_JSON : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/resource_picture.json',\r
92                 URL_COMICS_JSON               : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'comics.json',\r
93                 URL_MY_COMICS_JSON            : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/comic.json',\r
94                 URL_PANELS_JSON               : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'panels.json',\r
95                 URL_MY_PANELS_JSON            : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/panel.json',\r
96                 SPEECH_BALOON_TEMPLETE        : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'speech_balloon_templates.json\/',\r
97                 NS_PETTANR_COMIC              : 'pettanr-comic',\r
98                 THUMBNAIL_PATH                : SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/thumbnail\/' : PETTANR_ROOT_PATH + 'resource_pictures\/',\r
99                 RESOURCE_PICTURE_PATH         : SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/' : PETTANR_ROOT_PATH + 'resource_pictures\/full\/',\r
100                 ORIGINAL_PICTURE_PATH         : SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/' : PETTANR_ROOT_PATH + 'original_pictures\/',\r
101                 PICTURE_PATH                  : SERVER_SUPPORT === false ? RELATIVE + 'pictures\/' : PETTANR_ROOT_PATH + 'pictures\/',\r
102                 SYSTEM_PICTURE_PATH           : ( SERVER_SUPPORT === false ? RELATIVE : PETTANR_ROOT_PATH ) + 'system_pictures\/',\r
103                 CREATE_COMIC_JS               : SERVER_SUPPORT === false ? 'js\/create_new_comic.js' : PETTANR_ROOT_PATH + 'comics\/new.js',\r
104                 CREATE_PANEL_JS               : SERVER_SUPPORT === false ? 'js\/create_new_panel.js' : PETTANR_ROOT_PATH + 'panels\/new.js',\r
105                 UPDATE_PANEL_JS               : SERVER_SUPPORT === false ? 'js\/edit_panel.js?id=' : PETTANR_ROOT_PATH + 'panels\/',\r
106                 UPLOAD_PICTURE_JS             : SERVER_SUPPORT === false ? 'js\/upload_picture.js' : PETTANR_ROOT_PATH + 'original_pictures\/new.js',\r
107                 REGISTER_ARTIST_JS            : SERVER_SUPPORT === false ? 'js\/register_artist.js' : PETTANR_ROOT_PATH + 'artists\/new.js',\r
108                 SERVER_SUPPORT                : SERVER_SUPPORT,\r
109                 URL_PETA_APPS_CSS             : ( SERVER_SUPPORT === false ? 'stylesheets' : '\/assets' ) + '\/peta.apps.css'\r
110         }\r
111 })();\r
112 \r
113 /* ----------------------------------------\r
114  * old balloon.\r
115  * \r
116  */\r
117 pettanr.balloon = ( function() {\r
118         var MIN_BALLOON_WIDTH  = 30,\r
119                 MIN_BALLOON_HEIGHT = 30,\r
120                 NUM_BALLOON_IMAGE  = 24,\r
121                 TAIL_WIDTH         = 6,\r
122                 TAIL_HEIGHT        = 10,\r
123                 STROKE_WIDTH       = 1.2,\r
124                 PADDING_TOP        = TAIL_HEIGHT,\r
125                 PADDING_LEFT       = TAIL_HEIGHT,\r
126                 IS_VML             = UA.isIE === true && UA.ieVersion < 9,\r
127                 ELM_BALLOON_ORIGIN = ( function(){\r
128                         var ret;\r
129                         try {\r
130                                 if( IS_VML === true ){\r
131                                         ret = document.createElement( 'DIV');\r
132                                         var shape = document.createElement( 'v:shape');\r
133                                         shape.coordorigin = "0,0";\r
134                                         shape.strokecolor = "black";\r
135                                         shape.strokeweight = STROKE_WIDTH;\r
136                                         shape.fillcolor = "white";\r
137                                         ret.appendChild( shape);\r
138                                 } else {\r
139                                         var kSVGNS = 'http://www.w3.org/2000/svg';\r
140                                         // http://modernizr.com/downloads/modernizr.js\r
141                                         // Thanks to Erik Dahlstrom\r
142                                         if( !document.createElementNS || !document.createElementNS(kSVGNS, 'svg').createSVGRect ){\r
143                                                 return null;\r
144                                         };\r
145                                         ret = document.createElementNS( kSVGNS, 'svg');\r
146                                         var path = document.createElementNS( kSVGNS, 'path');\r
147                                         path.setAttribute( 'fill', "white");\r
148                                         path.setAttribute( 'stroke', "black");\r
149                                         path.setAttribute( 'strokeWidth', STROKE_WIDTH);\r
150                                         ret.appendChild( path );\r
151                                 };\r
152                                 return ret;     \r
153                         } catch( e){\r
154                                 return null;\r
155                         }\r
156                 })(),\r
157                 vectorEnabled = ELM_BALLOON_ORIGIN !== null &&\r
158                                                 pettanr.URL_PARAMS.vector !== false &&\r
159                                                 !( IS_VML === true && UA.VML === false );\r
160 \r
161         var cos        = Math.cos,\r
162                 sin        = Math.sin,\r
163                 abs        = function(v){ return v >= 0 ? v : -1; },\r
164                 pow        = Math.pow,\r
165                 round      = Math.round,\r
166                 floor      = Math.floor,\r
167                 TARGET     = TAIL_WIDTH * TAIL_WIDTH,\r
168                 isFinite   = Type.isFinite,\r
169                 ACCURACY   = 1, // 有効少数桁      \r
170                 cround     = function( v, r ){\r
171                                                 r = r || ACCURACY;\r
172                                                 return round( v * pow( 10.0, r )) / pow( 10.0, r );\r
173                                         },\r
174                 DEG_TO_RAD = Math.PI / 180;\r
175 \r
176         var XBROWSER_BALLOON_CLASS = function( w, h, a ){\r
177                 this.elm   = vectorEnabled === true ? ELM_BALLOON_ORIGIN.cloneNode( true ) : document.createElement( 'img' ); // pettanr.imageに変更\r
178                 this.path  = this.elm.getElementsByTagName( IS_VML === true ? 'shape' : 'path' )[ 0 ];\r
179                 this.resize( a, w, h );\r
180         };\r
181         XBROWSER_BALLOON_CLASS.prototype = {\r
182                 elm   : null,\r
183                 path  : null,\r
184                 w     : 0,\r
185                 h     : 0,\r
186                 a     : 0,\r
187                 resize : function ( _a, _w, _h ){\r
188                         this.w  = isFinite( _w ) === true ? _w - PADDING_TOP  * 2 : this.w;\r
189                         this.h  = isFinite( _h ) === true ? _h - PADDING_LEFT * 2 : this.h;\r
190                         // ie6 でリサイズが反応しない対策\r
191                         if( vectorEnabled === false && UA.isIE === true && UA.ieVersion < 7 ){\r
192                                 var parent = this.elm.parentNode;\r
193                                 parent.removeChild( this.elm );\r
194                                 parent.insertBefore( this.elm, parent.firstChild );\r
195                         };\r
196                         this.angle( _a );\r
197                 },\r
198                 angle : function( _a ){\r
199                         if( isFinite( _a ) === true ){\r
200                                 this.a = _a;\r
201                                 if( vectorEnabled === false ){\r
202                                         this.elm.src = pettanr.balloon.getBalloonUrl( this.w, this.h, _a );\r
203                                 } else {\r
204                                         this.draw( _a );\r
205                                 };\r
206                         };\r
207                         return this.a;\r
208                 },\r
209                 type : function( _type ){\r
210                         //draw( _a);\r
211                 },\r
212                 destroy : function(){\r
213                         this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
214                         delete this.elm;\r
215                         delete this.path;\r
216                 },\r
217                 draw : function( _a ){\r
218                         var rx      = this.w / 2,\r
219                                 ry      = this.h / 2,\r
220                                 l       = ',',\r
221                                 tailRad = _a * DEG_TO_RAD,\r
222                                 tailX   = rx + ( rx + TAIL_HEIGHT ) * sin( tailRad ),\r
223                                 tailY   = ry - ( ry + TAIL_HEIGHT ) * cos( tailRad ),\r
224                                 startX, startY, endX, endY;\r
225                 /*\r
226                  * tailの太さをTAIL_WIDTHに一致させるため、角度を絞りつつ計算\r
227                  */\r
228                         var startRad, endRad,\r
229                                 _startX, _startY, _endX, _endY,\r
230                                 tailDeg = 0, d;\r
231                         \r
232                         for( var i = 45; i > 0.01; i /= 2){\r
233                                 d = ( tailDeg + i ) / 2;\r
234                                 startRad = ( _a + d ) * DEG_TO_RAD;\r
235                                 endRad   = ( _a - d ) * DEG_TO_RAD;\r
236                                 \r
237                                 _startX  = rx + sin( startRad ) * rx;\r
238                                 _startY  = ry - cos( startRad ) * ry;\r
239                                 _endX    = rx + sin( endRad ) * rx;\r
240                                 _endY    = ry - cos( endRad ) * ry;     //円弧上のY位置=円中心Y+sin(角度×PI÷180)×円半径\r
241                                         \r
242                                 if( pow( ( _startX - _endX ), 2 ) + pow( ( _startY - _endY ), 2 ) < TARGET ){\r
243                                         tailDeg += i;\r
244                                         startX  = _startX;\r
245                                         startY  = _startY;\r
246                                         endX    = _endX;\r
247                                         endY    = _endY;\r
248                                 };\r
249                         };\r
250 \r
251                 /*\r
252                  * \r
253                  */                     \r
254                         if( IS_VML === true ){\r
255                                 var _tailX = tailX *10,\r
256                                         _tailY = tailY *10,\r
257                                         __w = this.w *10,\r
258                                         __h = this.h *10;\r
259                                 \r
260                                 this.path.style.width  = this.w + 'px';\r
261                                 this.path.style.height = this.h + 'px';\r
262                                 this.path.coordsize = [ __w, __h ].join( l );\r
263                                 this.path.path = [\r
264                                         ' ar ', 0, l, 0, l, __w, l, __h, l,\r
265                                         round( endX * 10 ), l, round( endY * 10 ), l,\r
266                                         round( startX * 10 ), l, round( startY * 10 ),\r
267                                         ' l ', round( _tailX ), l, round( _tailY ),\r
268                                         ' x e'\r
269                                 ].join( '' );\r
270 \r
271                                 this.elm.style.marginTop =  _tailY < 0 ? floor( ( 60 + _tailY) / 10 ) : 10;\r
272                                 this.elm.style.marginLeft = _tailX < 0 ? floor( ( 60 + _tailX) / 10 ) : 10;\r
273                         } else {\r
274                                 this.elm.setAttribute( 'width',  this.w + PADDING_LEFT *2 );\r
275                                 this.elm.setAttribute( 'height', this.h + PADDING_TOP *2 );\r
276                                 this.path.setAttribute( 'd', [\r
277                                         'M', cround( tailX + PADDING_LEFT ),  l, cround( tailY  + PADDING_TOP ),\r
278                                         'L', cround( startX + PADDING_LEFT ), l, cround( startY + PADDING_TOP ),\r
279                                         'A', rx, l, ry,\r
280                                         '0 1 1',                        // flag\r
281                                         cround( endX + PADDING_LEFT ), l, cround( endY + PADDING_TOP ),\r
282                                         'z'\r
283                                 ].join( ' '));\r
284                         }\r
285                 }\r
286         };\r
287         \r
288         return {\r
289             createBalloon: function( _w, _h, _a ){\r
290                 return new XBROWSER_BALLOON_CLASS( _w, _h, _a );\r
291             },\r
292             isBalloonInstance: function( _ballon ){\r
293                 \r
294             },\r
295             getBalloonUrl: function( _w, _h, _a ){\r
296                         var d = 360 / NUM_BALLOON_IMAGE;\r
297                         _a = _a + d / 2;\r
298                         return [\r
299                                 pettanr.CONST.SYSTEM_PICTURE_PATH, '_w',\r
300                                 _a < 360 - d / 2 ? floor( _a / d ) : 0,\r
301                                 _w <= 400 || _h <= 400 ? '_b1' : '',\r
302                                 '.gif' ].join( '' );\r
303             },\r
304                 TYPE_NONE:                              0,\r
305                 TYPE_SPEACH_BALLOON:    1,\r
306                 TYPE_THINKING:                  2,\r
307                 TYPE_BOM:                               3,\r
308                 TYPE_BLACK_BOX:                 4,\r
309                 TYPE_BLUE_BOX:                  5\r
310         }\r
311 })();\r
312 \r
313 /* ----------------------------------------\r
314  * New Balloon\r
315  * \r
316  * Vector : SVG, Canvas, VML, Flash, None\r
317  * \r
318  */\r
319 pettanr.newBalloon = ( function(){\r
320         var TEMPLETES = [];\r
321         \r
322         var PICTURE_PATH       = pettanr.CONST.SYSTEM_PICTURE_PATH,\r
323                 STROKE_WIDTH       = 1.2,\r
324                 IS_VML             = UA.isIE === true && UA.ieVersion < 9,\r
325                 ELM_BALLOON_ORIGIN = ( function(){\r
326                         var ret;\r
327                         try {\r
328                                 if( IS_VML === true ){\r
329                                         ret = document.createElement( 'DIV' );\r
330                                         var shape = document.createElement( 'v:shape' );\r
331                                         shape.coordorigin  = "0,0";\r
332                                         shape.strokecolor  = "black";\r
333                                         shape.strokeweight = STROKE_WIDTH;\r
334                                         shape.fillcolor    = "white";\r
335                                         ret.appendChild( shape );\r
336                                 } else {\r
337                                         var kSVGNS = 'http://www.w3.org/2000/svg';\r
338                                         // http://modernizr.com/downloads/modernizr.js\r
339                                         // Thanks to Erik Dahlstrom\r
340                                         if( !document.createElementNS || !document.createElementNS(kSVGNS, 'svg' ).createSVGRect ){\r
341                                                 return null;\r
342                                         };\r
343                                         ret = document.createElementNS( kSVGNS, 'svg' );\r
344                                         var path = document.createElementNS( kSVGNS, 'path' );\r
345                                         path.setAttribute( 'fill', "white" );\r
346                                         path.setAttribute( 'stroke', "black" );\r
347                                         path.setAttribute( 'strokeWidth', STROKE_WIDTH );\r
348                                         ret.appendChild( path );\r
349                                 };\r
350                                 return ret;     \r
351                         } catch( e ){\r
352                                 return null;\r
353                         };\r
354                 })(),\r
355                 vectorEnabled = ELM_BALLOON_ORIGIN !== null &&\r
356                                                 pettanr.URL_PARAMS.vector !== false &&\r
357                                                 !( IS_VML === true && UA.VML === false ),\r
358                 BalloonClass;\r
359         \r
360         if( vectorEnabled === true ){\r
361                 BalloonClass = function( klass ){\r
362                         this.elm     = ELM_BALLOON_ORIGIN.cloneNode( true );\r
363                         this.path    = this.elm.getElementsByTagName( IS_VML === true ? 'shape' : 'path' )[ 0 ];\r
364                         this.klass   = klass;\r
365                         this.getPath = klass.getPath;\r
366                 };\r
367                 BalloonClass.prototype = {\r
368                         elm     : null,\r
369                         path    : null,\r
370                         klass   : null,\r
371                         getPath : null,\r
372                         args    : null,\r
373                         update  : IS_VML === true ?\r
374                                 ( function( w, h, a /* w, h [, angle, ,,, ] */ ){\r
375                                         var w = arguments[ 0 ],\r
376                                                 h = arguments[ 1 ];\r
377                                         var path = this.getPath.apply( this.klass, arguments );\r
378                                         if( !path ) return false;\r
379                                         this.a = a;\r
380                                         path = /* SVG2VML */ path;\r
381                                         this.path.style.width  = w + 'px';\r
382                                         this.path.style.height = h + 'px';\r
383                                         this.path.coordsize    = ( w * 10 ) + ',' + ( h * 10 );\r
384                                         this.path.path         = path;\r
385                                         // this.elm.style.marginTop =  _tailY < 0 ? floor( ( 60 + _tailY) / 10 ) : 10;\r
386                                         // this.elm.style.marginLeft = _tailX < 0 ? floor( ( 60 + _tailX) / 10 ) : 10;\r
387                                 }) :\r
388                                 ( function( w, h, a /* w, h [, angle, ,,, ] */ ){\r
389                                         var w = arguments[ 0 ],\r
390                                                 h = arguments[ 1 ],\r
391                                                 d = this.getPath.apply( this.klass, arguments );\r
392                                         if( !d ) return false;\r
393                                         this.a = a;\r
394                                         this.elm.setAttribute( 'width',  w );\r
395                                         this.elm.setAttribute( 'height', h );\r
396                                         this.path.setAttribute( 'd', d );\r
397                                 }),\r
398                         destroy : function(){\r
399                                 this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
400                                 delete this.elm;\r
401                                 delete this.path;\r
402                         }\r
403                 };\r
404         } else {\r
405                 BalloonClass = function( klass ){\r
406                         // templete の vector の有無\r
407                         this.elm          = document.createElement( 'img' ); // pettanr.imageに変更\r
408                         this.klass        = klass;\r
409                         this.getPictureID = klass.getPictureID;\r
410                 };\r
411                 BalloonClass.prototype = {\r
412                         elm          : null,\r
413                         klass        : null,\r
414                         getPictureID : null,\r
415                         args         : null,\r
416                         update : function( w, h, a /* w, h [, angle, ,,, ] */ ){\r
417                                 var id = this.getPictureID.aplly( this.klass, arguments );\r
418                                 if( id !== 0 && !id ) return false;\r
419                                 this.a = a;\r
420                                 this.elm.src = PICTURE_PATH + id + '.gif';\r
421                         },\r
422                         destroy : function(){\r
423                                 this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
424                         }\r
425                 };\r
426         };\r
427         \r
428         /* js を持たず ハッシュに格納したデータと 1枚以上の system picture から描画を行う\r
429          * http://sourceforge.jp/projects/pettanr/wiki/HowToMakeSpeechBalloon\r
430          */\r
431         var NonVectorBalloonClass = function( templete ){\r
432                 this.elm       = document.createElement( 'img' ); // pettanr.imageに変更\r
433                 this.size      = this.elm.style;\r
434                 //this.templete  = templete;\r
435                 //this.speech    = templete.speech_template_attributes;\r
436                 //this.defaultW  = templete.default_width;\r
437                 //this.defaultH  = templete.default_height;\r
438                 //this.sizeCount = templete.size_count;\r
439                 var sb  = templete.speech_balloon;\r
440                 var v, p;\r
441                 \r
442                 this.wOffset   = sb.width_offset;\r
443                 this.hOffset   = sb.height_offset;\r
444                 this.wStep     = sb.width_step;\r
445                 this.hStep     = sb.height_step;\r
446 \r
447                 // 2013.3.31 itozyun's memo\r
448                 // 以下の使いやすく加工する処理は pettanr.balloon.register() 内に移動してもよさそう\r
449                 // new TempleteClass( templeteJson );\r
450                 // NonVectorBalloon.templete.defaultW みたいにアクセス\r
451                 if( sb.size_count && 1 < sb.size_count ){\r
452                         this.picIDs = []; // system picture id list\r
453                         for( p in templete ){\r
454                                 if( p === 'speech_balloon' ) continue;\r
455                                 if( '' + parseInt( p ) === p ) continue;\r
456                                 v = templete[ p ];\r
457                                 this.picIDs[ this.picIDs.length ] = v.balloon.system_picture_id;\r
458                         };\r
459                         if( !this.rOffset ) return;\r
460                         // tail\r
461                         //\r
462                 } else\r
463                 if( sb.r_offset && sb.r_step ){\r
464                         this.rOffset = sb.r_offset;\r
465                         this.rSteps  = sb.r_step;\r
466                         this.tailIDs = [];\r
467                         for( p in templete ){\r
468                                 if( '' + parseInt( p ) !== p ) continue;\r
469                                 v = templete[ p ];\r
470                                 this.tailIDs[ this.tailIDs.length ] = v.balloon.system_picture_id;\r
471                         };\r
472                 } else {\r
473                         for( p in templete ){\r
474                                 if( p === 'speech_balloon' ) continue;\r
475                                 if( '' + parseInt( p ) === p ) continue;\r
476                                 v = templete[ p ];\r
477                                 this.picID = v.balloon.system_picture_id;\r
478                                 break;\r
479                         };\r
480                 };\r
481         };\r
482         NonVectorBalloonClass.prototype = {\r
483                 elm      : null,\r
484                 size     : null,\r
485                 templete : null,\r
486                 args     : null,\r
487                 picIDs   : null,\r
488                 picID    : 0,\r
489                 tailIDs  : null,\r
490                 src      : 0,\r
491                 w        : 0,\r
492                 h        : 0,\r
493                 a        : 0,\r
494                 update : function( w, h, a /* [, ,,, ] */ ){\r
495                         var l, id, _w, _h, d;\r
496                         if( this.picIDs ){\r
497                                 _w = ( w - this.wOffset ) / this.wStep;\r
498                                 _h = ( h - this.hOffset ) / this.hStep;\r
499                                 l  = this.picIDs.length - 1;\r
500                                 _w = _w < 0 ? 0 : ( _w > l ? l : _w );\r
501                                 _h = _h < 0 ? 0 : ( _h > l ? l : _h );\r
502                                 id = this.picIDs[ Math.floor( _w > _h ? _w : _h ) ];\r
503                                 // tail\r
504                                 //\r
505                         } else\r
506                         if( this.tailIDs ){\r
507                                 this.a = a;\r
508                                 d  = this.rSteps;\r
509                                 a  = a + d / 2;\r
510                                 id = this.tailIDs[ a < 360 - d / 2 ? Math.floor( a / d ) : 0 ];\r
511                         } else {\r
512                                 id = this.picID;\r
513                         };\r
514 \r
515                         if( this.src !== id ){\r
516                                 this.elm.src = PICTURE_PATH + id + '.gif';\r
517                                 this.src = id;\r
518                         };\r
519                         if( this.w !== w ){\r
520                                 this.size.width = w + 'px';\r
521                                 this.w = w;\r
522                         };\r
523                         if( this.h !== h ){\r
524                                 this.size.height = h + 'px';\r
525                                 this.h = h;\r
526                         };\r
527                 },\r
528                 destroy : function(){\r
529                         this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
530                 }\r
531         };\r
532         \r
533         return {\r
534                 /**\r
535                  * balloon_templetes.json 取得時に呼ばれる\r
536                  * className は存在しない場合もある \r
537                  */\r
538                 register : function( id, name, templete ){\r
539                         templete = $.parseJSON( templete );\r
540                         if( !TEMPLETES[ name ] ){\r
541                                 TEMPLETES[ name ] = templete;\r
542                                 TEMPLETES[ id ]   = templete;\r
543                                 if( name in window ) templete.classname = name;\r
544                         };\r
545                 },\r
546                 create : function( idOrName, w, h, settings ){\r
547                         var ret,\r
548                                 tmpl = TEMPLETES[ idOrName ],\r
549                                 args = settings.split( ',' ),\r
550                                 l    = args.length,\r
551                                 i    = 0, v;\r
552                         if( tmpl.classname ){\r
553                                 ret = new BalloonClass( window[ tmpl.classname ] );\r
554                         } else {\r
555                                 ret = new NonVectorBalloonClass( tmpl );\r
556                         };\r
557                         for( ; i < l; ++i ){\r
558                                 v = args[ i ];\r
559                                 args[ i ] = parseInt( v );\r
560                         };\r
561                         args.unshift( h );\r
562                         args.unshift( w );\r
563                         ret.update.apply( ret, args );\r
564                         return ret;\r
565                 },\r
566                 isTemplete : function( templete ){\r
567                         \r
568                 },\r
569                 isInstance : function( balloon ){\r
570                         return balloon instanceof BalloonClass || balloon instanceof NonVectorBalloonClass;\r
571                 }\r
572         }\r
573 })();\r
574 \r
575 \r
576 /**\r
577  * Balloon \r
578  */\r
579 var Circle = {\r
580         getPath : function( w, h, a ){\r
581                 var TAIL_WIDTH   = 6,\r
582                         TAIL_HEIGHT  = 10,\r
583                         PADDING_TOP  = TAIL_HEIGHT,\r
584                         PADDING_LEFT = TAIL_HEIGHT,\r
585                         TARGET       = TAIL_WIDTH * TAIL_WIDTH,\r
586                         ACCURACY     = 1, // 有効少数桁    ;\r
587                         MATH         = Math,\r
588                         DEG_TO_RAD   = MATH.PI / 180,\r
589                         cos          = MATH.cos,\r
590                         sin          = MATH.sin,\r
591                         pow          = MATH.pow,\r
592                         round        = MATH.round,\r
593                         cround       = function( v, r ){\r
594                                 r = r || ACCURACY;\r
595                                 return round( v * pow( 10.0, r )) / pow( 10.0, r );\r
596                         },\r
597                         rx           = ( w - PADDING_LEFT * 2 ) / 2,\r
598                         ry           = ( h - PADDING_TOP  * 2 ) / 2,\r
599                         _            = ',',\r
600                         tailRad      = a * DEG_TO_RAD,\r
601                         tailX        = rx + ( rx + TAIL_HEIGHT ) * sin( tailRad ),\r
602                         tailY        = ry - ( ry + TAIL_HEIGHT ) * cos( tailRad ),\r
603                         tailDeg      = 0,\r
604                         d, startRad, endRad,\r
605                         startX, startY, endX, endY,\r
606                         _startX, _startY, _endX, _endY;\r
607                 /*\r
608                  * tailの太さをTAIL_WIDTHに一致させるため、角度を絞りつつ計算\r
609                  */\r
610                 for( var i = 45; i > 0.01; i /= 2 ){\r
611                         d = ( tailDeg + i ) / 2;\r
612                         startRad = ( a + d ) * DEG_TO_RAD;\r
613                         endRad   = ( a - d ) * DEG_TO_RAD;\r
614                         \r
615                         _startX  = rx + sin( startRad ) * rx;\r
616                         _startY  = ry - cos( startRad ) * ry;\r
617                         _endX    = rx + sin( endRad ) * rx;\r
618                         _endY    = ry - cos( endRad ) * ry;     //円弧上のY位置=円中心Y+sin(角度×PI÷180)×円半径\r
619                                 \r
620                         if( pow( ( _startX - _endX ), 2 ) + pow( ( _startY - _endY ), 2 ) < TARGET ){\r
621                                 tailDeg += i;\r
622                                 startX  = _startX;\r
623                                 startY  = _startY;\r
624                                 endX    = _endX;\r
625                                 endY    = _endY;\r
626                         };\r
627                 };\r
628 \r
629                 return [\r
630                         'M', cround( tailX + PADDING_LEFT  ), _, cround( tailY  + PADDING_TOP ),\r
631                         'L', cround( startX + PADDING_LEFT ), _, cround( startY + PADDING_TOP ),\r
632                         'A', rx, _, ry,\r
633                         '0 1 1',                        // flag\r
634                         cround( endX + PADDING_LEFT ), _, cround( endY + PADDING_TOP ),\r
635                         'z'\r
636                 ].join( ' ' );\r
637         },\r
638         getPictureID : function( w, h, a ){\r
639                 \r
640         }\r
641 };\r
642 \r
643 \r
644 \r
645 /* ----------------------------------------\r
646  *  pettanr.image\r
647  *  \r
648  *   xBackendな画像反転、画像描画機能。\r
649  *   \r
650  *   画像の反転\r
651  *     - css3\r
652  *     - ActiveX (ie)\r
653  *     - VML (ie)\r
654  *     - canvas ??\r
655  *     - flash(lite)\r
656  *     - silverlight\r
657  *     - pettan server\r
658  *   \r
659  *   png画像の表示(アルファpngをサポートしないie6以下のため)\r
660  *     - ActiveX\r
661  *     - VML\r
662  *     - flash(lite)\r
663  *     - silverlight\r
664  *     - pettan server\r
665  * \r
666  *     -moz-transform:scale( -1, -1);\r
667  */\r
668 pettanr.image = ( function(){\r
669         \r
670         var FetchImageControl = ( function(){\r
671                 var TASK_LIST = [];\r
672 \r
673                 /* \r
674                  * FetchClass original is\r
675                  * \r
676                  * LICENSE: MIT?\r
677                  *  URL: http://d.hatena.ne.jp/uupaa/20080413/1208067631\r
678                  *  AUTHOR: uupaa.js@gmail.com\r
679                  * \r
680                  */\r
681 \r
682                 var FetchClass = function( abspath, onLoadCallback, onErrorCallback, delay, timeout ){\r
683                         var img,\r
684                                 size,\r
685                                 tick = 0,\r
686                                 timer = null,\r
687                                 finish = false;\r
688                                 /*\r
689                         if( UA.isIE === false && UA.ieVersion < 8 ){\r
690                                 var images = document.images,\r
691                                         i=0, l= images.length;\r
692                                 for( i=0; i<l; ++i ){\r
693                                         img = images[ i ];\r
694                                         if( img.src === abspath && img.complete ){\r
695                                                 finish = true;\r
696                                                 size = Util.getImageSize( img );\r
697                                                 timer = window.setTimeout( asyncCallback, 0 );\r
698                                                 break;\r
699                                         }\r
700                                 }\r
701                                 images = null;\r
702                         }*/\r
703                         //if( finish === false ){\r
704                                 img = document.createElement( 'img' ); //var img = new Image(); ではieでimgのsizeが取れない、、、removeChildも失敗し、imgSizeGetterにimgが残る\r
705                                 img.onabort = img.onerror = onError;\r
706                                 img.onload = onLoad;\r
707                                 img.src = abspath;\r
708                                 finish === false && timeout && detect();\r
709                         //}\r
710                         \r
711                         function onError(){\r
712                                 if( finish === true ) return;\r
713                                 finish = true;\r
714                                 timer = window.setTimeout( asyncCallback, 10 );\r
715                         }                       \r
716                         function onLoad(){\r
717                                 // if( finish === true ) return; // これがあると firefox3.6 で駄目、、、\r
718                                 // if( timer ) return; // これがあると safari3.2 で駄目、、、\r
719                                 finish = true;\r
720                                 timer !== null && window.clearTimeout( timer );\r
721                                 if( window.opera && !img.complete ){\r
722                                         timer = window.setTimeout( asyncCallback, 10 );\r
723                                         return;\r
724                                 }\r
725                                 size = Util.getImageSize( img );\r
726                                 timer = window.setTimeout( asyncCallback, 10 );\r
727                         }\r
728                         function detect(){\r
729                                 if( finish === true ) return;\r
730                                 if( img.complete ){\r
731                                         finish = true;\r
732                                         if( img.width ) return;\r
733                                         timer = window.setTimeout( asyncCallback, 10 );\r
734                                         return;\r
735                                 }\r
736                                 if( ( tick += delay ) > timeout ){\r
737                                         finish = true;\r
738                                         timer = window.setTimeout( asyncCallback, 10 );\r
739                                         return;\r
740                                 }\r
741                                 timer = window.setTimeout( detect, delay );\r
742                         }\r
743                         \r
744                         function asyncCallback(){\r
745                                 size ? onLoadCallback( abspath, size.width, size.height ) : onErrorCallback( abspath );\r
746                                 destroy();\r
747                         }\r
748                         function destroy(){\r
749                                 finish  = true;\r
750                                 img.src = img.onload = img.onabort = img.onerror = '';\r
751                                 img     = void 0;\r
752                                 size    = onLoadCallback = onErrorCallback = timer = null;\r
753                         }\r
754                         this.stop = function(){\r
755                                 timer !== null && window.clearTimeout( timer );\r
756                                 destroy();                      \r
757                         }\r
758                 }\r
759                 \r
760                 return {\r
761                         load: function( URLorELM, onLoad, onError, delay, opt_timeout ){\r
762                                 var src, fetch;\r
763                                 if( Type.isString( URLorELM ) === true ){\r
764                                         src = URLorELM;\r
765                                 } else\r
766                                 if( Type.isHTMLElement( URLorELM ) === true && URLorELM.tagName.toLowerCase() === 'img' ){\r
767                                         src = URLorELM.src;\r
768                                 } else {\r
769                                         return;\r
770                                 }\r
771                                 \r
772                                 fetch = new FetchClass(\r
773                                         Util.getAbsolutePath( src ),\r
774                                         onLoad, onError,\r
775                                         Type.isFinite( delay ) === true ? delay : 250,\r
776                                         Type.isFinite( opt_timeout ) === true ? opt_timeout : undefined\r
777                                 );\r
778                                 // TASK_LIST.push( fetch );\r
779                                 \r
780                                 return fetch;\r
781                         }\r
782                 }\r
783         })();\r
784         \r
785         var REG_PNG           = /\.png?/i,\r
786                 IS_CSS3           = 0,\r
787                 IS_VML            = 1,\r
788                 IS_ACTIVEX        = 2,\r
789                 IS_CANVAS         = 3,\r
790                 IS_FLASH          = 4,\r
791                 IS_SILVERLIGHT    = 5,\r
792                 IS_SERVER         = 6,\r
793                 IS_ACTIVEX_SERVER = 7,\r
794                 BACKEND = ( function(){\r
795                         if( pettanr.DEBUG === true && pettanr.URL_PARAMS.rimg ){\r
796                                 var rimg = pettanr.URL_PARAMS.rimg.toLowerCase();\r
797                                 if( rimg === 'css3' ) return IS_CSS3;\r
798                                 if( rimg === 'activex' ) return IS_ACTIVEX;\r
799                                 if( rimg === 'vml' ) return IS_VML;\r
800                         }\r
801                         if( UA.isIE === false || UA.ieVersion >= 9 ) return IS_CSS3; // 不十分!\r
802                         if( UA.VML === true ) return IS_VML;\r
803                         if( UA.ACTIVEX === true ) return IS_ACTIVEX;\r
804                         if( pettanr.FLASH === true ) return IS_FLASH;\r
805                         if( pettanr.SILVERLIGHT === true ) return IS_SILVERLIGHT;\r
806                         return IS_SERVER;\r
807                 })(),\r
808                 BACKEND_WHEN_PNG = ( function(){\r
809                         if( UA.isIE === false || UA.ieVersion > 6 ) return BACKEND;\r
810                         if( UA.VML === true ) return IS_VML;\r
811                         if( pettanr.FLASH === true ) return IS_FLASH;\r
812                         if( pettanr.SILVERLIGHT === true ) return IS_SILVERLIGHT;\r
813                         if( UA.ACTIVEX === true ) return IS_ACTIVEX_SERVER;\r
814                         return IS_SERVER;\r
815                 })(),\r
816                 CLASS_NAME         = 'reversible-image-container',\r
817                 CLASS_NAME_LOADING = CLASS_NAME + ' loading',\r
818                 CLASS_NAME_ERROR   = CLASS_NAME +' error',\r
819                 RETRY_DELAY        = 5000,\r
820                 NUM_RETRY          = 3,\r
821                 ReversibleImageClass,\r
822                 ReversibleImageClassWithPingfix;\r
823         \r
824         var css3Image = function( url, w, h, onLoadCallback ){\r
825                 var elmWrap    = document.createElement( 'div' ),\r
826                         elmImg     = null,\r
827                         retryTimer = null,\r
828                         fetch      = FetchImageControl.load( url, onLoad, onError, 100, 10000 ),\r
829                         instance   = this;\r
830                 elmWrap.className = CLASS_NAME_LOADING;\r
831 \r
832                 function onLoad( _url, _actualW, _actualH ){\r
833                         if( elmWrap === null ) return;\r
834                         elmImg = new Image; // new Image でないと ie6,7 でクラッシュするかも、、、?\r
835                         /*\r
836                          * createElement 直後に append しないと、ie(ActiveX)で img が正しく表示されない.\r
837                          */\r
838                         elmWrap.appendChild( elmImg );\r
839                         elmImg.setAttribute( 'src', url );\r
840                         elmWrap.className = CLASS_NAME;\r
841                         onLoadCallback && onLoadCallback( _url, _actualW, _actualH );\r
842                         onLoadCallback = fetch = null;\r
843                         instance.resize( w, h );\r
844                 }\r
845                 function onError( _url ){\r
846                         if( elmWrap === null ) return;\r
847                         elmWrap.className = CLASS_NAME_ERROR;\r
848                         retryTimer = window.setTimeout( retry, RETRY_DELAY );\r
849                         fetch = null;\r
850                 }\r
851                 function retry(){\r
852                         elmWrap.className = CLASS_NAME_LOADING;\r
853                         fetch = FetchImageControl.load( url, onLoad, onError, 100, 10000 );\r
854                 }\r
855 \r
856                 this.elm = elmWrap;\r
857                 this.resize = function( _w, _h ){\r
858                         w = _w !== undefined ? _w : w;\r
859                         h = _h !== undefined ? _h : h;\r
860                         if( elmImg === null ) return;\r
861                         elmImg.className = w < 0 || h < 0 ? ( 'img-flip-' + ( w < 0 && h < 0 ? 'vh' : ( w < 0 ? 'h' : 'v'))) : '';\r
862                 }\r
863                 this.destroy = function(){\r
864                         delete instance.destroy;\r
865                         \r
866                         elmImg !== null && elmWrap.removeChild( elmImg );\r
867                         retryTimer !== null && window.clearTimeout( retryTimer );\r
868                         fetch !== null && fetch.stop();\r
869                         elmWrap = elmImg = onLoadCallback = retryTimer = fetch = instance = null;\r
870                 }\r
871         }\r
872                 \r
873         var activexImage = css3Image;\r
874         \r
875         var vmlImage = function( url, w, h, onLoadCallback ){\r
876                 var elmWrap = document.createElement( 'div' ),\r
877                         vmlImg = null,\r
878                         retryTimer = null,\r
879                         fetch = FetchImageControl.load( url, onLoad, onError, 100, 10000 ),\r
880                         instance = this;\r
881                 elmWrap.className = CLASS_NAME_LOADING;\r
882                 function onLoad( _url, _actualW, _actualH ){\r
883                         if( elmWrap === null ) return;\r
884                         elmWrap.className = CLASS_NAME;\r
885                         vmlImg = document.createElement( 'v:image' );\r
886                         vmlImg.src = url;\r
887                         onLoadCallback && onLoadCallback( _url, _actualW, _actualH );\r
888                         onLoadCallback = fetch = null;\r
889                         instance.resize( w, h );\r
890                 }\r
891                 function onError( _url ){\r
892                         if( elmWrap === null ) return;\r
893                         elmWrap.className = CLASS_NAME_ERROR;\r
894                         retryTimer = window.setTimeout( retry, RETRY_DELAY );\r
895                         fetch = null;\r
896                 }\r
897                 function retry(){\r
898                         elmWrap.className = CLASS_NAME_LOADING;\r
899                         fetch = FetchImageControl.load( url, onLoad, onError, 100, 10000 );\r
900                 }\r
901                 \r
902                 this.elm = elmWrap;\r
903                 this.resize = function( _w, _h ){\r
904                         w = _w !== undefined ? _w : w;\r
905                         h = _h !== undefined ? _h : h;\r
906                         if( vmlImg === null ) return;\r
907                         vmlImg.style.width  = ( w < 0 ? -w : w ) + 'px';\r
908                         vmlImg.style.height = ( h < 0 ? -h : h ) + 'px';\r
909                         //if( flipH !== _flipH || flipV !== _flipV){\r
910                                 vmlImg.parentNode === elmWrap && elmWrap.removeChild( vmlImg );\r
911                         //}\r
912                                 vmlImg.className = w < 0 || h < 0 ? ( 'img-flip-' + ( w < 0 && h < 0 ? 'vh' : ( w < 0 ? 'h' : 'v'))) : '';\r
913                                 elmWrap.appendChild( vmlImg );\r
914                 }\r
915                 this.destroy = function(){\r
916                         delete instance.destroy;\r
917                         \r
918                         vmlImg !== null && elmWrap.removeChild( vmlImg );\r
919                         retryTimer !== null && window.clearTimeout( retryTimer );\r
920                         fetch !== null && fetch.stop();\r
921                         elmWrap = vmlImg = onLoadCallback = retryTimer = fetch = instance = null;\r
922                 }\r
923         }\r
924         \r
925         var serverImage = css3Image; // function( url, w, h, onLoadCallback ){}\r
926         \r
927         if( BACKEND === IS_CSS3 )    ReversibleImageClass = css3Image;\r
928         if( BACKEND === IS_VML )     ReversibleImageClass = vmlImage;\r
929         if( BACKEND === IS_ACTIVEX ) ReversibleImageClass = activexImage;\r
930         if( BACKEND === IS_SERVER )  ReversibleImageClass = activexImage;\r
931         \r
932         css3Image = vmlImage = activexImage = activexImage = null;\r
933         \r
934         return {\r
935                 createReversibleImage: function( url, w, h, onLoadCallback){\r
936                         return new ReversibleImageClass( url, w, h, onLoadCallback );\r
937                 }\r
938         }\r
939 })();\r
940 \r
941 /*\r
942  * bind : 製本\r
943  */\r
944 pettanr.bind = ( function(){\r
945         var BIND_WORKER_ARRAY   = [],\r
946                 NAMESPACE_CLASSNAME = pettanr.CONST.NS_PETTANR_COMIC + '-',\r
947                 PICTURE_PATH        = pettanr.CONST.PICTURE_PATH,\r
948                 ELM_DETECT_WIDTH    = ( function(){\r
949                         var ret = document.createElement( 'div' );\r
950                         ret.style.cssText = 'width: auto;height: 0;padding: 0;margin: 0;display: block;visibility: hidden;float: none;position: static;';\r
951                         return ret;\r
952                 })(),\r
953                 ELM_TITLE_ORIGN = ( function(){\r
954                         \r
955                 })();\r
956 \r
957         /*\r
958          * original\r
959          *   http://d.hatena.ne.jp/uupaa/20090720/1248097177\r
960          */\r
961         var ResizeAgentClass = function( onResizeFunction, opt_elmCheck){\r
962                 var     _globalLock = 0,\r
963                         _size = { w: 0, h: 0 },\r
964                         _ie = !!document.all,\r
965                         _quirks = (document.compatMode || "") !== "CSS1Compat",\r
966                         _ieroot = _quirks ? "body" : "documentElement",\r
967                         _root = opt_elmCheck ? opt_elmCheck : ( _ie ? document[_ieroot] : window);\r
968 \r
969                 function getInnerSize(){\r
970                         return {\r
971                                 w: _root.innerWidth  || _root.clientWidth,\r
972                                 h: _root.innerHeight || _root.clientHeight\r
973                         };\r
974                 };\r
975 \r
976                 function loop(){\r
977                         if (!_globalLock++) {\r
978                                 var size = getInnerSize();\r
979                                 if (_size.w !== size.w || _size.h !== size.h) { // resized\r
980                                         _size = size; // update\r
981                                         onResizeFunction( _size );\r
982                                 };\r
983                                 setTimeout( unlock, 0); // delay unlock\r
984                         };\r
985                         setTimeout(loop, 500);\r
986                 };\r
987                 function unlock(){\r
988                         _globalLock = 0;\r
989                 };\r
990                 loop();\r
991         };\r
992         \r
993         \r
994         var ElementBuilder = function( elmTarget, noClassname ){\r
995                 this.imageList    = [];\r
996                 this.balloonList  = [];\r
997                 this.elmTarget    = elmTarget;\r
998                 this.noClassname  = noClassname;\r
999         };\r
1000         ElementBuilder.prototype = {\r
1001                 build : function( json, zoom ){\r
1002                         this.clean();\r
1003                         \r
1004                         // json is Comic ? Panel ?\r
1005                         var stories = json.children || json.stories,\r
1006                                 i, l;\r
1007                         if( Type.isArray( stories ) === true ){\r
1008                                 // comic\r
1009                                 for( i = 0, l = stories.length; i<l; ++i ){\r
1010                                         this.buildPanelElement( stories[ i ], zoom );\r
1011                                 };\r
1012                         } else\r
1013                         if( json.elements ){\r
1014                                 // panel\r
1015                                 this.buildPanelElement( json, zoom );\r
1016                         } else {\r
1017                                 // invalid json\r
1018                         };\r
1019                 },\r
1020                 buildFromFile : function( file, zoom ){\r
1021                         this.clean();\r
1022                         \r
1023                         var l    = file.getChildFileLength(),\r
1024                                 data = file.read(),\r
1025                                 i, story;\r
1026                         if( data.title ){\r
1027                                 // comic\r
1028                                 for( i = 0; i<l; ++i ){\r
1029                                         story = file.getChildFileAt( i ).read();\r
1030                                         this.buildPanelElement( story.panel, zoom );\r
1031                                 };\r
1032                         } else\r
1033                         if( data.panel ){\r
1034                                 // story\r
1035                                 this.buildPanelElement( data.panel, zoom );\r
1036                         } else\r
1037                         if( data.border ){\r
1038                                 // panel\r
1039                                 this.buildPanelElement( data, zoom );\r
1040                         } else {\r
1041                                 // invalid json\r
1042                         };\r
1043                 },\r
1044                 buildComicElement : function(){\r
1045                         \r
1046                 },\r
1047                 buildPanelElement : function( json, zoom ){\r
1048                         var elmPanel = document.createElement( 'div' ),\r
1049                                 style = {\r
1050                                                 'border-width':         typeof json.border === 'number' ? json.border + 'px' : 0,\r
1051                                                 width:                          json.width  + 'px',\r
1052                                                 height:                         json.height + 'px'\r
1053                                 },\r
1054                                 cssText  = [],\r
1055                                 elements = json.elements,\r
1056                                 realPic,\r
1057                                 p, i, l, data;\r
1058                         this.elmTarget.appendChild( elmPanel );\r
1059                         \r
1060                         if( this.noClassname === true ){\r
1061                                 \r
1062                         } else {\r
1063                                 elmPanel.className = NAMESPACE_CLASSNAME + 'panel';\r
1064                         };\r
1065                         for( p in style ){\r
1066                                 cssText.push( p + ':' + style[ p ] );\r
1067                         };\r
1068                         elmPanel.style.cssText = cssText.join( ';' );\r
1069                         \r
1070                         if( !elements ) return;\r
1071                         for( i = 0, l = elements.length; i < l; ++i ){\r
1072                                 data    = elements[ i ];\r
1073                                 if( !data.balloons ){\r
1074                                         this.buildImage( elmPanel, data, data.picture );\r
1075                                 } else {\r
1076                                         this.buildBalloon( elmPanel, data );\r
1077                                 };\r
1078                         };\r
1079                 },\r
1080                 buildImage : function( elmPanel, data, realPic ){\r
1081                         var rImg = pettanr.image.createReversibleImage(\r
1082                                         [ PICTURE_PATH, realPic.id, '.', realPic.ext ].join( '' ),\r
1083                                         data.width, data.height\r
1084                                 ),\r
1085                                 elmImg = rImg.elm;\r
1086                         elmPanel.appendChild( elmImg );\r
1087                         elmImg.className = NAMESPACE_CLASSNAME + 'image';\r
1088                         elmImg.style.cssText = [\r
1089                                 'left:',    data.x, 'px;',\r
1090                                 'top:',     data.y, 'px;',\r
1091                                 'z-index:', data.z, ';'\r
1092                         ].join( '');                                    \r
1093                         if( elmImg.tagName === 'img' ){\r
1094                                 elmImg.width        = Math.abs( data.width );\r
1095                                 elmImg.height       = Math.abs( data.height );\r
1096                         } else {\r
1097                                 elmImg.style.width  = Math.abs( data.width ) + 'px';\r
1098                                 elmImg.style.height = Math.abs( data.height ) + 'px';\r
1099                         };\r
1100                         \r
1101                         this.imageList.push( rImg );                    \r
1102                 },\r
1103                 buildBalloon : function( elmPanel, data ){\r
1104                         var elmBalloonWrap = document.createElement( 'div' ),\r
1105                                 elmText        = document.createElement( 'p' ),\r
1106                                 content        = '',\r
1107                                 balloons       = data.balloons,\r
1108                                 speeches       = data.speeches,\r
1109                                 balloonData,\r
1110                                 balloon, p;\r
1111                         elmPanel.appendChild( elmBalloonWrap );\r
1112                         elmBalloonWrap.className = NAMESPACE_CLASSNAME + 'balloon';\r
1113                         \r
1114                         if( balloons ){\r
1115                                 for( p in balloons ){\r
1116                                         balloonData = balloons[ p ];\r
1117                                         break;\r
1118                                 };\r
1119                                 if( balloonData ){\r
1120                                         elmBalloonWrap.style.cssText = [\r
1121                                                 'width:',   balloonData.width, 'px;',\r
1122                                                 'height:',  balloonData.height, 'px;',\r
1123                                                 'left:',    balloonData.x, 'px;',\r
1124                                                 'top:',     balloonData.y, 'px;',\r
1125                                                 'z-index:', data.z, ';'\r
1126                                         ].join( '' );                                   \r
1127                                 };\r
1128                                 \r
1129                                 balloon = pettanr.newBalloon.create( data.balloon_template.id, balloonData.width, balloonData.height, data.settings );\r
1130                                 elmBalloonWrap.appendChild( balloon.elm );\r
1131         \r
1132                                 elmBalloonWrap.appendChild( elmText );\r
1133                                 \r
1134                                 elmText.appendChild( document.createElement( 'span' ) );\r
1135                                 \r
1136                                 if( speeches ){\r
1137                                         for( p in speeches ){\r
1138                                                 content += speeches[ p ] && speeches[ p ].content ? speeches[ p ].content : '';\r
1139                                         };\r
1140                                 };\r
1141                                 elmText.firstChild.appendChild( document.createTextNode( content ));\r
1142                                 this.balloonList.push( balloon );                               \r
1143                         };\r
1144                 },\r
1145                 clean : function(){\r
1146                         // clean elmTarget\r
1147                         while( this.imageList.length > 0 ){\r
1148                                 this.imageList.shift().destroy();\r
1149                         };\r
1150                         while( this.balloonList.length > 0 ){\r
1151                                 this.balloonList.shift().destroy();\r
1152                         };\r
1153                         Util.removeAllChildren( this.elmTarget );\r
1154                 },\r
1155                 zoom : function(){},\r
1156                 destroy : function(){\r
1157                         this.clean();\r
1158                 }                       \r
1159         };\r
1160         \r
1161         var BindWorker = function( elmTarget, json, zoomSelfEnabled, noClassname ){\r
1162                 this.builder     = new ElementBuilder( elmTarget, noClassname );\r
1163                 this.elmDetectW  = ELM_DETECT_WIDTH.cloneNode( false );\r
1164                 this._json        = json;\r
1165                 this.noClassname = noClassname;\r
1166                 if( zoomSelfEnabled === true ){\r
1167                         elmTarget.parentNode.insertBefore( this.elmDetectW, elmTarget );\r
1168                         this.resizer = new ResizeAgentClass( onResize, this.elmDetectW );\r
1169                 };\r
1170                 function onResize(){\r
1171                         \r
1172                 };\r
1173                 json && typeof json === 'object' && this.builder.build( json );\r
1174         };\r
1175         BindWorker.prototype = {\r
1176                 init : function(){\r
1177                                 \r
1178                 },\r
1179                 zoom : function(){\r
1180                         this.builder.zoom();\r
1181                 },\r
1182                 json : function( json ){\r
1183                         this._json = json;\r
1184                         this.builder.build( json, this.noClassname );\r
1185                 },\r
1186                 file : function( file ){\r
1187                         this.builder.buildFromFile( file, this.noClassname );\r
1188                 },\r
1189                 targetElement : function(){\r
1190                                 \r
1191                 },\r
1192                 layout : function(){\r
1193                                 \r
1194                 },\r
1195                 destroy : function(){\r
1196                         this.builder.destroy();\r
1197                         delete this.json;\r
1198                         delete this.builder;\r
1199                 }               \r
1200         };\r
1201         return {\r
1202                 createBindWorker: function( elmTarget, opt_COMICJSONorPANELJSON, opt_zoomSelfEnabled, opt_noClassnameMode ){\r
1203                         var ret = new BindWorker( elmTarget, opt_COMICJSONorPANELJSON, !!opt_zoomSelfEnabled, !!opt_noClassnameMode );\r
1204                         BIND_WORKER_ARRAY.push( ret );\r
1205                         return ret;\r
1206                 },\r
1207                 isBindWorkerInstance: function( _bindWorker ){\r
1208                         return _bindWorker instanceof BindWorkerClass;\r
1209                 }\r
1210         }\r
1211 })();\r
1212 \r
1213 \r
1214 /*\r
1215  *  Google Analytics\r
1216  */\r
1217 \r
1218 if( pettanr.LOCAL === false || document.location.href.indexOf( 'localhost:' ) === -1 ){\r
1219         var _gaq = _gaq || [];\r
1220         _gaq.push(['_setAccount', 'UA-28023955-1']);\r
1221         _gaq.push(['_trackPageview']);\r
1222         \r
1223         ( function(){\r
1224                 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\r
1225                 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';\r
1226                 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\r
1227         })();   \r
1228 };