OSDN Git Service

Client is version 0.5.56, working svg circle.
[pettanr/pettanr.git] / app / assets / javascripts / peta-common.js
index 94de697..fee0491 100644 (file)
@@ -1,6 +1,6 @@
 /*\r
  * pettanR peta.common.js\r
- *   version 0.5.40\r
+ *   version 0.5.56\r
  * \r
  *   author:\r
  *     itozyun\r
@@ -84,52 +84,34 @@ pettanr.CONST = ( function(){
                })();\r
                \r
        return {\r
-               PETTANR_ROOT_PATH:                      PETTANR_ROOT_PATH,\r
-               URL_ORIGINAL_PICTURES_JSON: ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'original_pictures.json',\r
-               URL_RESOURCE_PICTURES_JSON: ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'resource_pictures.json',\r
-               URL_MY_RESOURCE_PICTURES_JSON: ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/resource_picture.json',\r
-               URL_COMICS_JSON:                        ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'comics.json',\r
-               URL_MY_COMICS_JSON:                     ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/comic.json',\r
-               URL_PANELS_JSON:                        ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'panels.json',\r
-               URL_MY_PANELS_JSON:                     ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/panel.json',\r
-               NS_PETTANR_COMIC:                       'pettanr-comic',\r
-               THUMBNAIL_PATH:             SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/thumbnail\/' : PETTANR_ROOT_PATH + 'resource_pictures\/',\r
-               RESOURCE_PICTURE_PATH:          SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/' : PETTANR_ROOT_PATH + 'resource_pictures\/full\/',\r
-               PANEL_PICTURE_PATH:                 SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/' : PETTANR_ROOT_PATH + 'pictures\/',\r
-               SYSTEM_PICTURE_PATH:            ( SERVER_SUPPORT === false ? RELATIVE : PETTANR_ROOT_PATH ) + 'system_pictures\/',\r
-               CREATE_COMIC_JS:                        SERVER_SUPPORT === false ? 'js\/create_new_comic.js' : PETTANR_ROOT_PATH + 'comics\/new.js',\r
-               CREATE_PANEL_JS:                        SERVER_SUPPORT === false ? 'js\/create_new_panel.js' : PETTANR_ROOT_PATH + 'panels\/new.js',\r
-               UPLOAD_PICTURE_JS:                      SERVER_SUPPORT === false ? 'js\/upload_picture.js' : PETTANR_ROOT_PATH + 'original_pictures\/new.js',\r
-               REGISTER_ARTIST_JS:                     SERVER_SUPPORT === false ? 'js\/register_artist.js' : PETTANR_ROOT_PATH + 'artists\/new.js',\r
-               SERVER_SUPPORT:                         SERVER_SUPPORT,\r
-               URL_PETA_APPS_CSS:                      ( SERVER_SUPPORT === false ? 'stylesheets' : '\/assets' ) + '\/peta.apps.css'\r
+               PETTANR_ROOT_PATH             : PETTANR_ROOT_PATH,\r
+               URL_MY_ORIGINAL_PICTURES_JSON : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'original_pictures.json',\r
+               URL_MY_RESOURCE_PICTURES_JSON : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/resource_pictures.json',\r
+               URL_RESOURCE_PICTURES_JSON    : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'resource_pictures.json',\r
+               URL_MY_RESOURCE_PICTURES_JSON : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/resource_picture.json',\r
+               URL_COMICS_JSON               : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'comics.json',\r
+               URL_MY_COMICS_JSON            : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/comic.json',\r
+               URL_PANELS_JSON               : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'panels.json',\r
+               URL_MY_PANELS_JSON            : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'home\/panel.json',\r
+               SPEECH_BALOON_TEMPLETE        : ( SERVER_SUPPORT === false ? 'json\/' : PETTANR_ROOT_PATH ) + 'speech_balloon_templates.json\/',\r
+               NS_PETTANR_COMIC              : 'pettanr-comic',\r
+               THUMBNAIL_PATH                : SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/thumbnail\/' : PETTANR_ROOT_PATH + 'resource_pictures\/',\r
+               RESOURCE_PICTURE_PATH         : SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/' : PETTANR_ROOT_PATH + 'resource_pictures\/full\/',\r
+               ORIGINAL_PICTURE_PATH         : SERVER_SUPPORT === false ? RELATIVE + 'resource_pictures\/' : PETTANR_ROOT_PATH + 'original_pictures\/',\r
+               PICTURE_PATH                  : SERVER_SUPPORT === false ? RELATIVE + 'pictures\/' : PETTANR_ROOT_PATH + 'pictures\/',\r
+               SYSTEM_PICTURE_PATH           : ( SERVER_SUPPORT === false ? RELATIVE : PETTANR_ROOT_PATH ) + 'system_pictures\/',\r
+               CREATE_COMIC_JS               : SERVER_SUPPORT === false ? 'js\/create_new_comic.js' : PETTANR_ROOT_PATH + 'comics\/new.js',\r
+               CREATE_PANEL_JS               : SERVER_SUPPORT === false ? 'js\/create_new_panel.js' : PETTANR_ROOT_PATH + 'panels\/new.js',\r
+               UPDATE_PANEL_JS               : SERVER_SUPPORT === false ? 'js\/edit_panel.js?id=' : PETTANR_ROOT_PATH + 'panels\/',\r
+               UPLOAD_PICTURE_JS             : SERVER_SUPPORT === false ? 'js\/upload_picture.js' : PETTANR_ROOT_PATH + 'original_pictures\/new.js',\r
+               REGISTER_ARTIST_JS            : SERVER_SUPPORT === false ? 'js\/register_artist.js' : PETTANR_ROOT_PATH + 'artists\/new.js',\r
+               SERVER_SUPPORT                : SERVER_SUPPORT,\r
+               URL_PETA_APPS_CSS             : ( SERVER_SUPPORT === false ? 'stylesheets' : '\/assets' ) + '\/peta.apps.css'\r
        }\r
 })();\r
 \r
 /* ----------------------------------------\r
- * Vector Support\r
- * \r
- *              __________\r
- *             /          \\r
- *            /            \\r
- *            |,startX,Y    |\r
- * tailX,Y - <              |\r
- *            |'endX,Y      |\r
- *            \            /\r
- *                \__________/\r
- * \r
- * SVG\r
- * -----------------------\r
- * ie9, other modern browser\r
- * \r
- * XML\r
- * -----------------------\r
- * ie5.5-8\r
- * \r
- * 内部の角度計算は radian で統一したい。\r
- * 当初 vectorEnabled = true で一度書いてみる。\r
- * 駄目なら、代替のイメージのsrcの用意もここで担当。\r
- * 閲覧と編集両方で使う。\r
+ * old balloon.\r
  * \r
  */\r
 pettanr.balloon = ( function() {\r
@@ -145,7 +127,7 @@ pettanr.balloon = ( function() {
                ELM_BALLOON_ORIGIN = ( function(){\r
                        var ret;\r
                        try {\r
-                               if( IS_VML === true){\r
+                               if( IS_VML === true ){\r
                                        ret = document.createElement( 'DIV');\r
                                        var shape = document.createElement( 'v:shape');\r
                                        shape.coordorigin = "0,0";\r
@@ -183,24 +165,59 @@ pettanr.balloon = ( function() {
                round      = Math.round,\r
                floor      = Math.floor,\r
                TARGET     = TAIL_WIDTH * TAIL_WIDTH,\r
-               isFinit    = Type.isFinite,\r
+               isFinite   = Type.isFinite,\r
                ACCURACY   = 1, // 有効少数桁      \r
-               cround     = function ( v, r ){\r
+               cround     = function( v, r ){\r
                                                r = r || ACCURACY;\r
                                                return round( v * pow( 10.0, r )) / pow( 10.0, r );\r
                                        },\r
                DEG_TO_RAD = Math.PI / 180;\r
 \r
        var XBROWSER_BALLOON_CLASS = function( w, h, a ){\r
-               var balloonElm = vectorEnabled === true ? ELM_BALLOON_ORIGIN.cloneNode( true ) : document.createElement( 'img' ), // pettanr.imageに変更\r
-                       path = balloonElm.getElementsByTagName( 'path' )[ 0 ],\r
-                       shape = balloonElm.getElementsByTagName( 'shape' )[ 0 ],\r
-                       instance = this,\r
-                       l = ',';\r
-               \r
-               function draw( _a ){\r
-                       var rx      = w / 2,\r
-                               ry      = h / 2,\r
+               this.elm   = vectorEnabled === true ? ELM_BALLOON_ORIGIN.cloneNode( true ) : document.createElement( 'img' ); // pettanr.imageに変更\r
+               this.path  = this.elm.getElementsByTagName( IS_VML === true ? 'shape' : 'path' )[ 0 ];\r
+               this.resize( a, w, h );\r
+       };\r
+       XBROWSER_BALLOON_CLASS.prototype = {\r
+               elm   : null,\r
+               path  : null,\r
+               w     : 0,\r
+               h     : 0,\r
+               a     : 0,\r
+               resize : function ( _a, _w, _h ){\r
+                       this.w  = isFinite( _w ) === true ? _w - PADDING_TOP  * 2 : this.w;\r
+                       this.h  = isFinite( _h ) === true ? _h - PADDING_LEFT * 2 : this.h;\r
+                       // ie6 でリサイズが反応しない対策\r
+                       if( vectorEnabled === false && UA.isIE === true && UA.ieVersion < 7 ){\r
+                               var parent = this.elm.parentNode;\r
+                               parent.removeChild( this.elm );\r
+                               parent.insertBefore( this.elm, parent.firstChild );\r
+                       };\r
+                       this.angle( _a );\r
+               },\r
+               angle : function( _a ){\r
+                       if( isFinite( _a ) === true ){\r
+                               this.a = _a;\r
+                               if( vectorEnabled === false ){\r
+                                       this.elm.src = pettanr.balloon.getBalloonUrl( this.w, this.h, _a );\r
+                               } else {\r
+                                       this.draw( _a );\r
+                               };\r
+                       };\r
+                       return this.a;\r
+               },\r
+               type : function( _type ){\r
+                       //draw( _a);\r
+               },\r
+               destroy : function(){\r
+                       this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
+                       delete this.elm;\r
+                       delete this.path;\r
+               },\r
+               draw : function( _a ){\r
+                       var rx      = this.w / 2,\r
+                               ry      = this.h / 2,\r
+                               l       = ',',\r
                                tailRad = _a * DEG_TO_RAD,\r
                                tailX   = rx + ( rx + TAIL_HEIGHT ) * sin( tailRad ),\r
                                tailY   = ry - ( ry + TAIL_HEIGHT ) * cos( tailRad ),\r
@@ -213,7 +230,7 @@ pettanr.balloon = ( function() {
                                tailDeg = 0, d;\r
                        \r
                        for( var i = 45; i > 0.01; i /= 2){\r
-                               d = ( tailDeg + i ) /2;\r
+                               d = ( tailDeg + i ) / 2;\r
                                startRad = ( _a + d ) * DEG_TO_RAD;\r
                                endRad   = ( _a - d ) * DEG_TO_RAD;\r
                                \r
@@ -237,27 +254,27 @@ pettanr.balloon = ( function() {
                        if( IS_VML === true ){\r
                                var _tailX = tailX *10,\r
                                        _tailY = tailY *10,\r
-                                       __w = w *10,\r
-                                       __h = h *10;\r
+                                       __w = this.w *10,\r
+                                       __h = this.h *10;\r
                                \r
-                               shape.style.width = w + 'px';\r
-                               shape.style.height = h + 'px';\r
-                               shape.coordsize = [ __w, __h ].join( l );\r
-                               shape.path = [\r
+                               this.path.style.width  = this.w + 'px';\r
+                               this.path.style.height = this.h + 'px';\r
+                               this.path.coordsize = [ __w, __h ].join( l );\r
+                               this.path.path = [\r
                                        ' ar ', 0, l, 0, l, __w, l, __h, l,\r
                                        round( endX * 10 ), l, round( endY * 10 ), l,\r
                                        round( startX * 10 ), l, round( startY * 10 ),\r
                                        ' l ', round( _tailX ), l, round( _tailY ),\r
                                        ' x e'\r
-                               ].join( '');\r
+                               ].join( '' );\r
 \r
-                               balloonElm.style.marginTop =  _tailY < 0 ? floor( ( 60 + _tailY) / 10 ) : 10;\r
-                               balloonElm.style.marginLeft = _tailX < 0 ? floor( ( 60 + _tailX) / 10 ) : 10;\r
+                               this.elm.style.marginTop =  _tailY < 0 ? floor( ( 60 + _tailY) / 10 ) : 10;\r
+                               this.elm.style.marginLeft = _tailX < 0 ? floor( ( 60 + _tailX) / 10 ) : 10;\r
                        } else {\r
-                               balloonElm.setAttribute( 'width', w + PADDING_LEFT *2 );\r
-                               balloonElm.setAttribute( 'height', h + PADDING_TOP *2 );\r
-                               path.setAttribute( 'd', [\r
-                                       'M', cround( tailX + PADDING_LEFT ), l, cround( tailY + PADDING_TOP ),\r
+                               this.elm.setAttribute( 'width',  this.w + PADDING_LEFT *2 );\r
+                               this.elm.setAttribute( 'height', this.h + PADDING_TOP *2 );\r
+                               this.path.setAttribute( 'd', [\r
+                                       'M', cround( tailX + PADDING_LEFT ),  l, cround( tailY  + PADDING_TOP ),\r
                                        'L', cround( startX + PADDING_LEFT ), l, cround( startY + PADDING_TOP ),\r
                                        'A', rx, l, ry,\r
                                        '0 1 1',                        // flag\r
@@ -266,40 +283,6 @@ pettanr.balloon = ( function() {
                                ].join( ' '));\r
                        }\r
                }\r
-               \r
-               this.elm = balloonElm;\r
-               this.resize = function ( _a, _w, _h ){\r
-                       w  = isFinit( _w ) === true ? _w - PADDING_TOP * 2 : w;\r
-                       h  = isFinit( _h ) === true ? _h - PADDING_LEFT * 2 : h;\r
-                       // ie6 でリサイズが反応しない対策\r
-                       if( vectorEnabled === false && UA.isIE === true && UA.ieVersion < 7 ){\r
-                               var parent = balloonElm.parentNode;\r
-                               parent.removeChild( balloonElm );\r
-                               parent.insertBefore( balloonElm, parent.firstChild );\r
-                       }\r
-                       instance.angle( _a );\r
-               };\r
-               this.angle = function( _a ){\r
-                       if( isFinit( _a ) === true ){\r
-                               a = _a;\r
-                               if( vectorEnabled === false ){\r
-                                       balloonElm.src = pettanr.balloon.getBalloonUrl( w, h, _a );\r
-                               } else {\r
-                                       draw( _a );\r
-                               }\r
-                       }\r
-                       return a;\r
-               }\r
-               this.type = function( _type ){\r
-                       //draw( _a);\r
-               }\r
-               this.destroy = function(){\r
-                       delete instance.destroy;\r
-                       balloonElm.parentNode && balloonElm.parentNode.removeChild( balloonElm );\r
-                       balloonElm = path = shape = instance = null;\r
-               }\r
-               \r
-               instance.resize( a, w, h );\r
        };\r
        \r
        return {\r
@@ -328,6 +311,338 @@ pettanr.balloon = ( function() {
 })();\r
 \r
 /* ----------------------------------------\r
+ * New Balloon\r
+ * \r
+ * Vector : SVG, Canvas, VML, Flash, None\r
+ * \r
+ */\r
+pettanr.newBalloon = ( function(){\r
+       var TEMPLETES = [];\r
+       \r
+       var PICTURE_PATH       = pettanr.CONST.SYSTEM_PICTURE_PATH,\r
+               STROKE_WIDTH       = 1.2,\r
+               IS_VML             = UA.isIE === true && UA.ieVersion < 9,\r
+               ELM_BALLOON_ORIGIN = ( function(){\r
+                       var ret;\r
+                       try {\r
+                               if( IS_VML === true ){\r
+                                       ret = document.createElement( 'DIV' );\r
+                                       var shape = document.createElement( 'v:shape' );\r
+                                       shape.coordorigin  = "0,0";\r
+                                       shape.strokecolor  = "black";\r
+                                       shape.strokeweight = STROKE_WIDTH;\r
+                                       shape.fillcolor    = "white";\r
+                                       ret.appendChild( shape );\r
+                               } else {\r
+                                       var kSVGNS = 'http://www.w3.org/2000/svg';\r
+                                       // http://modernizr.com/downloads/modernizr.js\r
+                                       // Thanks to Erik Dahlstrom\r
+                                       if( !document.createElementNS || !document.createElementNS(kSVGNS, 'svg' ).createSVGRect ){\r
+                                               return null;\r
+                                       };\r
+                                       ret = document.createElementNS( kSVGNS, 'svg' );\r
+                                       var path = document.createElementNS( kSVGNS, 'path' );\r
+                                       path.setAttribute( 'fill', "white" );\r
+                                       path.setAttribute( 'stroke', "black" );\r
+                                       path.setAttribute( 'strokeWidth', STROKE_WIDTH );\r
+                                       ret.appendChild( path );\r
+                               };\r
+                               return ret;     \r
+                       } catch( e ){\r
+                               return null;\r
+                       };\r
+               })(),\r
+               vectorEnabled = ELM_BALLOON_ORIGIN !== null &&\r
+                                               pettanr.URL_PARAMS.vector !== false &&\r
+                                               !( IS_VML === true && UA.VML === false ),\r
+               BalloonClass;\r
+       \r
+       if( vectorEnabled === true ){\r
+               BalloonClass = function( klass ){\r
+                       this.elm     = ELM_BALLOON_ORIGIN.cloneNode( true );\r
+                       this.path    = this.elm.getElementsByTagName( IS_VML === true ? 'shape' : 'path' )[ 0 ];\r
+                       this.klass   = klass;\r
+                       this.getPath = klass.getPath;\r
+               };\r
+               BalloonClass.prototype = {\r
+                       elm     : null,\r
+                       path    : null,\r
+                       klass   : null,\r
+                       getPath : null,\r
+                       args    : null,\r
+                       update  : IS_VML === true ?\r
+                               ( function( w, h, a /* w, h [, angle, ,,, ] */ ){\r
+                                       var w = arguments[ 0 ],\r
+                                               h = arguments[ 1 ];\r
+                                       var path = this.getPath.apply( this.klass, arguments );\r
+                                       if( !path ) return false;\r
+                                       this.a = a;\r
+                                       path = /* SVG2VML */ path;\r
+                                       this.path.style.width  = w + 'px';\r
+                                       this.path.style.height = h + 'px';\r
+                                       this.path.coordsize    = ( w * 10 ) + ',' + ( h * 10 );\r
+                                       this.path.path         = path;\r
+                                       // this.elm.style.marginTop =  _tailY < 0 ? floor( ( 60 + _tailY) / 10 ) : 10;\r
+                                       // this.elm.style.marginLeft = _tailX < 0 ? floor( ( 60 + _tailX) / 10 ) : 10;\r
+                               }) :\r
+                               ( function( w, h, a /* w, h [, angle, ,,, ] */ ){\r
+                                       var w = arguments[ 0 ],\r
+                                               h = arguments[ 1 ],\r
+                                               d = this.getPath.apply( this.klass, arguments );\r
+                                       if( !d ) return false;\r
+                                       this.a = a;\r
+                                       this.elm.setAttribute( 'width',  w );\r
+                                       this.elm.setAttribute( 'height', h );\r
+                                       this.path.setAttribute( 'd', d );\r
+                               }),\r
+                       destroy : function(){\r
+                               this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
+                               delete this.elm;\r
+                               delete this.path;\r
+                       }\r
+               };\r
+       } else {\r
+               BalloonClass = function( klass ){\r
+                       // templete の vector の有無\r
+                       this.elm          = document.createElement( 'img' ); // pettanr.imageに変更\r
+                       this.klass        = klass;\r
+                       this.getPictureID = klass.getPictureID;\r
+               };\r
+               BalloonClass.prototype = {\r
+                       elm          : null,\r
+                       klass        : null,\r
+                       getPictureID : null,\r
+                       args         : null,\r
+                       update : function( w, h, a /* w, h [, angle, ,,, ] */ ){\r
+                               var id = this.getPictureID.aplly( this.klass, arguments );\r
+                               if( id !== 0 && !id ) return false;\r
+                               this.a = a;\r
+                               this.elm.src = PICTURE_PATH + id + '.gif';\r
+                       },\r
+                       destroy : function(){\r
+                               this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
+                       }\r
+               };\r
+       };\r
+       \r
+       /* js を持たず ハッシュに格納したデータと 1枚以上の system picture から描画を行う\r
+        * http://sourceforge.jp/projects/pettanr/wiki/HowToMakeSpeechBalloon\r
+        */\r
+       var NonVectorBalloonClass = function( templete ){\r
+               this.elm       = document.createElement( 'img' ); // pettanr.imageに変更\r
+               this.size      = this.elm.style;\r
+               //this.templete  = templete;\r
+               //this.speech    = templete.speech_template_attributes;\r
+               //this.defaultW  = templete.default_width;\r
+               //this.defaultH  = templete.default_height;\r
+               //this.sizeCount = templete.size_count;\r
+               var sb  = templete.speech_balloon;\r
+               var v, p;\r
+               \r
+               this.wOffset   = sb.width_offset;\r
+               this.hOffset   = sb.height_offset;\r
+               this.wStep     = sb.width_step;\r
+               this.hStep     = sb.height_step;\r
+\r
+               // 2013.3.31 itozyun's memo\r
+               // 以下の使いやすく加工する処理は pettanr.balloon.register() 内に移動してもよさそう\r
+               // new TempleteClass( templeteJson );\r
+               // NonVectorBalloon.templete.defaultW みたいにアクセス\r
+               if( sb.size_count && 1 < sb.size_count ){\r
+                       this.picIDs = []; // system picture id list\r
+                       for( p in templete ){\r
+                               if( p === 'speech_balloon' ) continue;\r
+                               if( '' + parseInt( p ) === p ) continue;\r
+                               v = templete[ p ];\r
+                               this.picIDs[ this.picIDs.length ] = v.balloon.system_picture_id;\r
+                       };\r
+                       if( !this.rOffset ) return;\r
+                       // tail\r
+                       //\r
+               } else\r
+               if( sb.r_offset && sb.r_step ){\r
+                       this.rOffset = sb.r_offset;\r
+                       this.rSteps  = sb.r_step;\r
+                       this.tailIDs = [];\r
+                       for( p in templete ){\r
+                               if( '' + parseInt( p ) !== p ) continue;\r
+                               v = templete[ p ];\r
+                               this.tailIDs[ this.tailIDs.length ] = v.balloon.system_picture_id;\r
+                       };\r
+               } else {\r
+                       for( p in templete ){\r
+                               if( p === 'speech_balloon' ) continue;\r
+                               if( '' + parseInt( p ) === p ) continue;\r
+                               v = templete[ p ];\r
+                               this.picID = v.balloon.system_picture_id;\r
+                               break;\r
+                       };\r
+               };\r
+       };\r
+       NonVectorBalloonClass.prototype = {\r
+               elm      : null,\r
+               size     : null,\r
+               templete : null,\r
+               args     : null,\r
+               picIDs   : null,\r
+               picID    : 0,\r
+               tailIDs  : null,\r
+               src      : 0,\r
+               w        : 0,\r
+               h        : 0,\r
+               a        : 0,\r
+               update : function( w, h, a /* [, ,,, ] */ ){\r
+                       var l, id, _w, _h, d;\r
+                       if( this.picIDs ){\r
+                               _w = ( w - this.wOffset ) / this.wStep;\r
+                               _h = ( h - this.hOffset ) / this.hStep;\r
+                               l  = this.picIDs.length - 1;\r
+                               _w = _w < 0 ? 0 : ( _w > l ? l : _w );\r
+                               _h = _h < 0 ? 0 : ( _h > l ? l : _h );\r
+                               id = this.picIDs[ Math.floor( _w > _h ? _w : _h ) ];\r
+                               // tail\r
+                               //\r
+                       } else\r
+                       if( this.tailIDs ){\r
+                               this.a = a;\r
+                               d  = this.rSteps;\r
+                               a  = a + d / 2;\r
+                               id = this.tailIDs[ a < 360 - d / 2 ? Math.floor( a / d ) : 0 ];\r
+                       } else {\r
+                               id = this.picID;\r
+                       };\r
+\r
+                       if( this.src !== id ){\r
+                               this.elm.src = PICTURE_PATH + id + '.gif';\r
+                               this.src = id;\r
+                       };\r
+                       if( this.w !== w ){\r
+                               this.size.width = w + 'px';\r
+                               this.w = w;\r
+                       };\r
+                       if( this.h !== h ){\r
+                               this.size.height = h + 'px';\r
+                               this.h = h;\r
+                       };\r
+               },\r
+               destroy : function(){\r
+                       this.elm.parentNode && this.elm.parentNode.removeChild( this.elm );\r
+               }\r
+       };\r
+       \r
+       return {\r
+               /**\r
+                * balloon_templetes.json 取得時に呼ばれる\r
+                * className は存在しない場合もある \r
+                */\r
+               register : function( id, name, templete ){\r
+                       templete = $.parseJSON( templete );\r
+                       if( !TEMPLETES[ name ] ){\r
+                               TEMPLETES[ name ] = templete;\r
+                               TEMPLETES[ id ]   = templete;\r
+                               if( name in window ) templete.classname = name;\r
+                       };\r
+               },\r
+               create : function( idOrName, w, h, settings ){\r
+                       var ret,\r
+                               tmpl = TEMPLETES[ idOrName ],\r
+                               args = settings.split( ',' ),\r
+                               l    = args.length,\r
+                               i    = 0, v;\r
+                       if( tmpl.classname ){\r
+                               ret = new BalloonClass( window[ tmpl.classname ] );\r
+                       } else {\r
+                               ret = new NonVectorBalloonClass( tmpl );\r
+                       };\r
+                       for( ; i < l; ++i ){\r
+                               v = args[ i ];\r
+                               args[ i ] = parseInt( v );\r
+                       };\r
+                       args.unshift( h );\r
+                       args.unshift( w );\r
+                       ret.update.apply( ret, args );\r
+                       return ret;\r
+               },\r
+               isTemplete : function( templete ){\r
+                       \r
+               },\r
+               isInstance : function( balloon ){\r
+                       return balloon instanceof BalloonClass || balloon instanceof NonVectorBalloonClass;\r
+               }\r
+       }\r
+})();\r
+\r
+\r
+/**\r
+ * Balloon \r
+ */\r
+var Circle = {\r
+       getPath : function( w, h, a ){\r
+               var TAIL_WIDTH   = 6,\r
+                       TAIL_HEIGHT  = 10,\r
+                       PADDING_TOP  = TAIL_HEIGHT,\r
+                       PADDING_LEFT = TAIL_HEIGHT,\r
+                       TARGET       = TAIL_WIDTH * TAIL_WIDTH,\r
+                       ACCURACY     = 1, // 有効少数桁    ;\r
+                       MATH         = Math,\r
+                       DEG_TO_RAD   = MATH.PI / 180,\r
+                       cos          = MATH.cos,\r
+                       sin          = MATH.sin,\r
+                       pow          = MATH.pow,\r
+                       round        = MATH.round,\r
+                       cround       = function( v, r ){\r
+                               r = r || ACCURACY;\r
+                               return round( v * pow( 10.0, r )) / pow( 10.0, r );\r
+                       },\r
+                       rx           = ( w - PADDING_LEFT * 2 ) / 2,\r
+                       ry           = ( h - PADDING_TOP  * 2 ) / 2,\r
+                       _            = ',',\r
+                       tailRad      = a * DEG_TO_RAD,\r
+                       tailX        = rx + ( rx + TAIL_HEIGHT ) * sin( tailRad ),\r
+                       tailY        = ry - ( ry + TAIL_HEIGHT ) * cos( tailRad ),\r
+                       tailDeg      = 0,\r
+                       d, startRad, endRad,\r
+                       startX, startY, endX, endY,\r
+                       _startX, _startY, _endX, _endY;\r
+               /*\r
+                * tailの太さをTAIL_WIDTHに一致させるため、角度を絞りつつ計算\r
+                */\r
+               for( var i = 45; i > 0.01; i /= 2 ){\r
+                       d = ( tailDeg + i ) / 2;\r
+                       startRad = ( a + d ) * DEG_TO_RAD;\r
+                       endRad   = ( a - d ) * DEG_TO_RAD;\r
+                       \r
+                       _startX  = rx + sin( startRad ) * rx;\r
+                       _startY  = ry - cos( startRad ) * ry;\r
+                       _endX    = rx + sin( endRad ) * rx;\r
+                       _endY    = ry - cos( endRad ) * ry;     //円弧上のY位置=円中心Y+sin(角度×PI÷180)×円半径\r
+                               \r
+                       if( pow( ( _startX - _endX ), 2 ) + pow( ( _startY - _endY ), 2 ) < TARGET ){\r
+                               tailDeg += i;\r
+                               startX  = _startX;\r
+                               startY  = _startY;\r
+                               endX    = _endX;\r
+                               endY    = _endY;\r
+                       };\r
+               };\r
+\r
+               return [\r
+                       'M', cround( tailX + PADDING_LEFT  ), _, cround( tailY  + PADDING_TOP ),\r
+                       'L', cround( startX + PADDING_LEFT ), _, cround( startY + PADDING_TOP ),\r
+                       'A', rx, _, ry,\r
+                       '0 1 1',                        // flag\r
+                       cround( endX + PADDING_LEFT ), _, cround( endY + PADDING_TOP ),\r
+                       'z'\r
+               ].join( ' ' );\r
+       },\r
+       getPictureID : function( w, h, a ){\r
+               \r
+       }\r
+};\r
+\r
+\r
+\r
+/* ----------------------------------------\r
  *  pettanr.image\r
  *  \r
  *   xBackendな画像反転、画像描画機能。\r
@@ -346,7 +661,8 @@ pettanr.balloon = ( function() {
  *     - VML\r
  *     - flash(lite)\r
  *     - silverlight\r
- *     \r
+ *     - pettan server\r
+ * \r
  *     -moz-transform:scale( -1, -1);\r
  */\r
 pettanr.image = ( function(){\r
@@ -628,7 +944,7 @@ pettanr.image = ( function(){
 pettanr.bind = ( function(){\r
        var BIND_WORKER_ARRAY   = [],\r
                NAMESPACE_CLASSNAME = pettanr.CONST.NS_PETTANR_COMIC + '-',\r
-               PICTURE_PATH        = pettanr.CONST.PANEL_PICTURE_PATH,\r
+               PICTURE_PATH        = pettanr.CONST.PICTURE_PATH,\r
                ELM_DETECT_WIDTH    = ( function(){\r
                        var ret = document.createElement( 'div' );\r
                        ret.style.cssText = 'width: auto;height: 0;padding: 0;margin: 0;display: block;visibility: hidden;float: none;position: static;';\r
@@ -644,10 +960,10 @@ pettanr.bind = ( function(){
         */\r
        var ResizeAgentClass = function( onResizeFunction, opt_elmCheck){\r
                var     _globalLock = 0,\r
-                       _size = { w: 0, h: 0 };\r
+                       _size = { w: 0, h: 0 },\r
                        _ie = !!document.all,\r
                        _quirks = (document.compatMode || "") !== "CSS1Compat",\r
-                       _ieroot = _quirks ? "body" : "documentElement";\r
+                       _ieroot = _quirks ? "body" : "documentElement",\r
                        _root = opt_elmCheck ? opt_elmCheck : ( _ie ? document[_ieroot] : window);\r
 \r
                function getInnerSize(){\r
@@ -686,12 +1002,12 @@ pettanr.bind = ( function(){
                        this.clean();\r
                        \r
                        // json is Comic ? Panel ?\r
-                       var panels = json.children || json.stories,\r
+                       var stories = json.children || json.stories,\r
                                i, l;\r
-                       if( Type.isArray( panels ) === true ){\r
+                       if( Type.isArray( stories ) === true ){\r
                                // comic\r
-                               for( i = 0, l = panels.length; i<l; ++i ){\r
-                                       this.buildPanelElement( panels[ i ], zoom );\r
+                               for( i = 0, l = stories.length; i<l; ++i ){\r
+                                       this.buildPanelElement( stories[ i ], zoom );\r
                                };\r
                        } else\r
                        if( json.elements ){\r
@@ -701,6 +1017,30 @@ pettanr.bind = ( function(){
                                // invalid json\r
                        };\r
                },\r
+               buildFromFile : function( file, zoom ){\r
+                       this.clean();\r
+                       \r
+                       var l    = file.getChildFileLength(),\r
+                               data = file.read(),\r
+                               i, story;\r
+                       if( data.title ){\r
+                               // comic\r
+                               for( i = 0; i<l; ++i ){\r
+                                       story = file.getChildFileAt( i ).read();\r
+                                       this.buildPanelElement( story.panel, zoom );\r
+                               };\r
+                       } else\r
+                       if( data.panel ){\r
+                               // story\r
+                               this.buildPanelElement( data.panel, zoom );\r
+                       } else\r
+                       if( data.border ){\r
+                               // panel\r
+                               this.buildPanelElement( data, zoom );\r
+                       } else {\r
+                               // invalid json\r
+                       };\r
+               },\r
                buildComicElement : function(){\r
                        \r
                },\r
@@ -711,10 +1051,10 @@ pettanr.bind = ( function(){
                                                width:                          json.width  + 'px',\r
                                                height:                         json.height + 'px'\r
                                },\r
-                               cssText = [],\r
-                               elements = json.elements || ( json.panel ? json.panel.elements : [] ),\r
-                               rPic,\r
-                               p, i, l;\r
+                               cssText  = [],\r
+                               elements = json.elements,\r
+                               realPic,\r
+                               p, i, l, data;\r
                        this.elmTarget.appendChild( elmPanel );\r
                        \r
                        if( this.noClassname === true ){\r
@@ -727,19 +1067,19 @@ pettanr.bind = ( function(){
                        };\r
                        elmPanel.style.cssText = cssText.join( ';' );\r
                        \r
+                       if( !elements ) return;\r
                        for( i = 0, l = elements.length; i < l; ++i ){\r
-                               data = elements[ i ];\r
-                               rPic = data.picture;\r
-                               if( rPic ){\r
-                                       this.buildImage( elmPanel, data, rPic );\r
+                               data    = elements[ i ];\r
+                               if( !data.balloons ){\r
+                                       this.buildImage( elmPanel, data, data.picture );\r
                                } else {\r
                                        this.buildBalloon( elmPanel, data );\r
                                };\r
                        };\r
                },\r
-               buildImage : function( elmPanel, data, rPic ){\r
+               buildImage : function( elmPanel, data, realPic ){\r
                        var rImg = pettanr.image.createReversibleImage(\r
-                                       [ PICTURE_PATH, data.picture_id, '.', rPic.ext ].join( '' ),\r
+                                       [ PICTURE_PATH, realPic.id, '.', realPic.ext ].join( '' ),\r
                                        data.width, data.height\r
                                ),\r
                                elmImg = rImg.elm;\r
@@ -764,32 +1104,43 @@ pettanr.bind = ( function(){
                        var elmBalloonWrap = document.createElement( 'div' ),\r
                                elmText        = document.createElement( 'p' ),\r
                                content        = '',\r
-                               speechesAttr   = data.speeches_attributes,\r
+                               balloons       = data.balloons,\r
+                               speeches       = data.speeches,\r
+                               balloonData,\r
                                balloon, p;\r
                        elmPanel.appendChild( elmBalloonWrap );\r
                        elmBalloonWrap.className = NAMESPACE_CLASSNAME + 'balloon';\r
-                       elmBalloonWrap.style.cssText = [\r
-                               'width:',   data.width, 'px;',\r
-                               'height:',  data.height, 'px;',\r
-                               'left:',    data.x, 'px;',\r
-                               'top:',     data.y, 'px;',\r
-                               'z-index:', data.z, ';'\r
-                       ].join( '');\r
-\r
-                       balloon = pettanr.balloon.createBalloon( data.width, data.height, data.tail );\r
-                       elmBalloonWrap.appendChild( balloon.elm );\r
-                       \r
-                       elmBalloonWrap.appendChild( elmText );\r
                        \r
-                       elmText.appendChild( document.createElement( 'span' ) );\r
-                       \r
-                       if( speechesAttr ){\r
-                               for( p in speechesAttr ){\r
-                                       content += speechesAttr[ p ] && speechesAttr[ p ].content ? speechesAttr[ p ].content : '';\r
+                       if( balloons ){\r
+                               for( p in balloons ){\r
+                                       balloonData = balloons[ p ];\r
+                                       break;\r
+                               };\r
+                               if( balloonData ){\r
+                                       elmBalloonWrap.style.cssText = [\r
+                                               'width:',   balloonData.width, 'px;',\r
+                                               'height:',  balloonData.height, 'px;',\r
+                                               'left:',    balloonData.x, 'px;',\r
+                                               'top:',     balloonData.y, 'px;',\r
+                                               'z-index:', data.z, ';'\r
+                                       ].join( '' );                                   \r
                                };\r
+                               \r
+                               balloon = pettanr.newBalloon.create( data.balloon_template.id, balloonData.width, balloonData.height, data.settings );\r
+                               elmBalloonWrap.appendChild( balloon.elm );\r
+       \r
+                               elmBalloonWrap.appendChild( elmText );\r
+                               \r
+                               elmText.appendChild( document.createElement( 'span' ) );\r
+                               \r
+                               if( speeches ){\r
+                                       for( p in speeches ){\r
+                                               content += speeches[ p ] && speeches[ p ].content ? speeches[ p ].content : '';\r
+                                       };\r
+                               };\r
+                               elmText.firstChild.appendChild( document.createTextNode( content ));\r
+                               this.balloonList.push( balloon );                               \r
                        };\r
-                       elmText.firstChild.appendChild( document.createTextNode( content ));\r
-                       this.balloonList.push( balloon );\r
                },\r
                clean : function(){\r
                        // clean elmTarget\r
@@ -799,7 +1150,7 @@ pettanr.bind = ( function(){
                        while( this.balloonList.length > 0 ){\r
                                this.balloonList.shift().destroy();\r
                        };\r
-                       Util.removeAllChildren( this.elmTarget );       \r
+                       Util.removeAllChildren( this.elmTarget );\r
                },\r
                zoom : function(){},\r
                destroy : function(){\r
@@ -832,6 +1183,9 @@ pettanr.bind = ( function(){
                        this._json = json;\r
                        this.builder.build( json, this.noClassname );\r
                },\r
+               file : function( file ){\r
+                       this.builder.buildFromFile( file, this.noClassname );\r
+               },\r
                targetElement : function(){\r
                                \r
                },\r