/** * * 6dn PageFlip *---------------------------------------------------------------- * @notice 6dn PageFlip翻页类 * @author 6dn * @as version3.0 * @date 2009-1-4 * * AUTHOR ****************************************************************************** * * authorName : 黎新苑 - www.6dn.cn * QQ :160379558(小星@6dn) * MSN :xdngo@hotmail.com * email :6dn@6dn.cn * webpage : http://www.6dn.cn * * LICENSE ****************************************************************************** * * ① 此类是在AS3基础上编写,只能对使用as3的swf文件完全支持! * ② 基本上实现了现有的杂志功能,支持显示阴影,支持拖动翻页以及点击翻页,支持单页和双页显示,支持页面跳转; * ③ 使用内部xml或外部xml,支持外部读取jpg、gif、png、swf并可混合使用; * ④ 可扩展实现缩略图预览,可扩展添加loading; * ⑤ 可自由设置Timer,值越小翻页越流畅,值越大占用CPU越小; * ⑥ 此类作为开源使用,但请重视作者劳动成果,请使用此类的朋友保留作者信息。 * Please, keep this header and the list of all authors * */ package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.display.MovieClip; import flash.display.Stage; import flash.display.Loader; import flash.display.Shape; import flash.display.GradientType; import flash.display.SpreadMethod; import flash.geom.Point; import flash.geom.Matrix; import flash.filters.DropShadowFilter; import flash.net.URLRequest; import flash.utils.Timer; import flash.utils.*; import flash.events.Event; import flash.events.MouseEvent; import flash.events.ProgressEvent; import flash.events.TimerEvent; public class PageFlipClass { //可设置或可调用接口,页数以单页数计算~--------------------------------------- public var myXML:XML; public var book_root:MovieClip;//装载book的MC public var book_initpage:Number=0;//初始化到第N页 public var book_totalpage:Number=0;//总页数 public var book_TimerNum:Number=30;//Timer的间隔时间 public var book_page:Number=0;//当前页 public var onLoadinit:Function=null;//加载外部影片或图片时调用 public var onLoading:Function=null;//正在加载外部影片或图片时调用 public var onLoadEnd:Function=null;//加载外部影片或图片完毕时调用 public var onPageEnd:Function=null;//翻页动作结束时调用 //PageGoto:Function;//翻页跳转 //PageDraw:Function;//绘制缩略图 //InitBook:Function;//初始化 //END!!-------------------------------------------------------------------- private var book_width:Number; private var book_height:Number; private var book_topage:Number; private var book_CrossGap:Number; private var bookArray_layer1:Array; private var bookArray_layer2:Array; private var book_TimerFlag:String="stop"; private var book_TimerArg0:Number=0; private var book_TimerArg1:Number=0; private var u:Number; private var book_px:Number=0; private var book_py:Number=0; private var book_toposArray:Array; private var book_myposArray:Array; private var book_timer:Timer; private var Bmp0:BitmapData; private var Bmp1:BitmapData; private var bgBmp0:BitmapData; private var bgBmp1:BitmapData; private var pageMC:Sprite=new Sprite(); private var bgMC:Sprite=new Sprite(); private var render0:Shape=new Shape(); private var render1:Shape=new Shape(); private var shadow0:Shape=new Shape(); private var shadow1:Shape=new Shape(); private var Mask0:Shape=new Shape(); private var Mask1:Shape=new Shape(); var p1:Point; var p2:Point; var p3:Point; var p4:Point; var limit_point1:Point; var limit_point2:Point; //**init Parts------------------------------------------------------------------------ public function InitBook():void { book_width=myXML.attribute("width"); book_height=myXML.attribute("height"); book_totalpage=myXML.child("page").length(); book_page=book_topage=book_initpage; book_CrossGap=Math.sqrt(book_width*book_width+book_height*book_height); p1=new Point(0,0); p2=new Point(0,book_height); p3=new Point(book_width+book_width,0); p4=new Point(book_width+book_width,book_height); limit_point1=new Point(book_width,0); limit_point2=new Point(book_width,book_height); book_toposArray=[p3,p4,p1,p2]; book_myposArray=[p1,p2,p3,p4]; book_root.addChild(pageMC); book_root.addChild(bgMC); SeFilter(pageMC); SeFilter(bgMC); book_root.addChild(Mask0); book_root.addChild(Mask1); book_root.addChild(render0); book_root.addChild(shadow0); book_root.addChild(render1); book_root.addChild(shadow1); SetLoadMC(); SetPageMC(book_page); book_timer= new Timer(book_TimerNum, 0); book_root.stage.addEventListener(MouseEvent.MOUSE_DOWN, MouseOnDown); book_root.stage.addEventListener(MouseEvent.MOUSE_UP, MouseOnUp); book_timer.addEventListener("timer", bookTimerHandler); } //End init------------------------------------------------------------------------ //**DrawPage Parts------------------------------------------------------------------------ private function DrawPage(num:Number,_movePoint:Point,bmp1:BitmapData,bmp2:BitmapData):void { //var _movePoint:Point=new Point(mouseX,mouseY); var _actionPoint:Point; var book_array:Array; var book_Matrix1:Matrix=new Matrix(); var book_Matrix2:Matrix=new Matrix(); var Matrix_angle:Number; if (num==1) { _movePoint=CheckLimit(_movePoint,limit_point1,book_width); _movePoint=CheckLimit(_movePoint,limit_point2,book_CrossGap); book_array=GetBook_array(_movePoint,p1,p2); _actionPoint=book_array[1]; GeLayer_array(_movePoint,_actionPoint,p1,p2,limit_point1,limit_point2); DrawShadowShap(shadow0,Mask0,book_width*1.5,book_height*4,p1,_movePoint,new Array(p1,p3,p4,p2),0.5); DrawShadowShap(shadow1,Mask1,book_width*1.5,book_height*4,p1,_movePoint,bookArray_layer1,0.45); Matrix_angle=angle(_movePoint,_actionPoint)+90; book_Matrix1.rotate((Matrix_angle/180)*Math.PI); book_Matrix1.tx=book_array[3].x; book_Matrix1.ty=book_array[3].y; book_Matrix2.tx=p1.x; book_Matrix2.ty=p1.y; } else if (num==2) { _movePoint=CheckLimit(_movePoint,limit_point2,book_width); _movePoint=CheckLimit(_movePoint,limit_point1,book_CrossGap); book_array=GetBook_array(_movePoint,p2,p1); _actionPoint=book_array[1]; GeLayer_array(_movePoint,_actionPoint,p2,p1,limit_point2,limit_point1); DrawShadowShap(shadow0,Mask0,book_width*1.5,book_height*4,p2,_movePoint,new Array(p1,p3,p4,p2),0.5); DrawShadowShap(shadow1,Mask1,book_width*1.5,book_height*4,p2,_movePoint,bookArray_layer1,0.45); Matrix_angle=angle(_movePoint,_actionPoint)-90; book_Matrix1.rotate((Matrix_angle/180)*Math.PI); book_Matrix1.tx=book_array[2].x; book_Matrix1.ty=book_array[2].y; book_Matrix2.tx=p1.x; book_Matrix2.ty=p1.y; } else if (num==3) { _movePoint=CheckLimit(_movePoint,limit_point1,book_width); _movePoint=CheckLimit(_movePoint,limit_point2,book_CrossGap); book_array=GetBook_array(_movePoint,p3,p4); _actionPoint=book_array[1]; GeLayer_array(_movePoint,_actionPoint,p3,p4,limit_point1,limit_point2); DrawShadowShap(shadow0,Mask0,book_width*1.5,book_height*4,p3,_movePoint,new Array(p1,p3,p4,p2),0.5); DrawShadowShap(shadow1,Mask1,book_width*1.5,book_height*4,p3,_movePoint,bookArray_layer1,0.4); Matrix_angle=angle(_movePoint,_actionPoint)+90; book_Matrix1.rotate((Matrix_angle/180)*Math.PI); book_Matrix1.tx=_movePoint.x; book_Matrix1.ty=_movePoint.y; book_Matrix2.tx=limit_point1.x; book_Matrix2.ty=limit_point1.y; } else { _movePoint=CheckLimit(_movePoint,limit_point2,book_width); _movePoint=CheckLimit(_movePoint,limit_point1,book_CrossGap); book_array=GetBook_array(_movePoint,p4,p3); _actionPoint=book_array[1]; GeLayer_array(_movePoint,_actionPoint,p4,p3,limit_point2,limit_point1); DrawShadowShap(shadow0,Mask0,book_width*1.5,book_height*4,p4,_movePoint,new Array(p1,p3,p4,p2),0.5); DrawShadowShap(shadow1,Mask1,book_width*1.5,book_height*4,p4,_movePoint,bookArray_layer1,0.4); Matrix_angle=angle(_movePoint,_actionPoint)-90; book_Matrix1.rotate((Matrix_angle/180)*Math.PI); book_Matrix1.tx=_actionPoint.x; book_Matrix1.ty=_actionPoint.y; book_Matrix2.tx=limit_point1.x; book_Matrix2.ty=limit_point1.y; } //trace(bookArray_layer2) // DrawShape(render1,bookArray_layer1,bmp1,book_Matrix1); DrawShape(render0,bookArray_layer2,bmp2,book_Matrix2); } private function CheckLimit($point:Point,$limitPoint:Point,$limitGap:Number):Point { var $Gap:Number=Math.abs(pos($limitPoint,$point)); var $Angle:Number=angle($limitPoint,$point); if ($Gap>$limitGap) { var $tmp1:Number=$limitGap*Math.sin(($Angle/180)*Math.PI); var $tmp2:Number=$limitGap*Math.cos(($Angle/180)*Math.PI); $point=new Point($limitPoint.x-$tmp2,$limitPoint.y-$tmp1); } return $point; } private function GetBook_array($point:Point,$actionPoint1:Point,$actionPoint2:Point):Array { var array_return:Array=new Array(); var $Gap1:Number=Math.abs(pos($actionPoint1,$point)*0.5); var $Angle1:Number=angle($actionPoint1,$point); var $tmp1_2:Number=$Gap1/Math.cos(($Angle1/180)*Math.PI); var $tmp_point1:Point=new Point($actionPoint1.x-$tmp1_2,$actionPoint1.y); var $Angle2:Number=angle($point,$tmp_point1)-angle($point,$actionPoint2); var $Gap2:Number=pos($point,$actionPoint2); var $tmp2_1:Number=$Gap2*Math.sin(($Angle2/180)*Math.PI); var $tmp2_2:Number=$Gap2*Math.cos(($Angle2/180)*Math.PI); var $tmp_point2:Point=new Point($actionPoint1.x+$tmp2_2,$actionPoint1.y+$tmp2_1); var $Angle3:Number=angle($tmp_point1,$point); var $tmp3_1:Number=book_width*Math.sin(($Angle3/180)*Math.PI); var $tmp3_2:Number=book_width*Math.cos(($Angle3/180)*Math.PI); var $tmp_point3:Point=new Point($tmp_point2.x+$tmp3_2,$tmp_point2.y+$tmp3_1); var $tmp_point4:Point=new Point($point.x+$tmp3_2,$point.y+$tmp3_1); array_return.push($point); array_return.push($tmp_point2); array_return.push($tmp_point3); array_return.push($tmp_point4); return array_return; } private function GeLayer_array($point1:Point,$point2:Point,$actionPoint1:Point,$actionPoint2:Point,$limitPoint1:Point,$limitPoint2:Point):void { var array_layer1:Array=new Array(); var array_layer2:Array=new Array(); var $Gap1:Number=Math.abs(pos($actionPoint1,$point1)*0.5); var $Angle1:Number=angle($actionPoint1,$point1); var $tmp1_1:Number=$Gap1/Math.sin(($Angle1/180)*Math.PI); var $tmp1_2:Number=$Gap1/Math.cos(($Angle1/180)*Math.PI); var $tmp_point1:Point=new Point($actionPoint1.x-$tmp1_2,$actionPoint1.y); var $tmp_point2:Point=new Point($actionPoint1.x,$actionPoint1.y-$tmp1_1); var $tmp_point3=$point2; var $Gap2:Number=Math.abs(pos($point1,$actionPoint2)); //--------------------------------------------- if ($Gap2>book_height) { array_layer1.push($tmp_point3); // var $pos:Number=Math.abs(pos($tmp_point3,$actionPoint2)*0.5); var $tmp3:Number=$pos/Math.cos(($Angle1/180)*Math.PI); $tmp_point2=new Point($actionPoint2.x-$tmp3,$actionPoint2.y); } else { array_layer2.push($actionPoint2); } array_layer1.push($tmp_point2); array_layer1.push($tmp_point1); array_layer1.push($point1); bookArray_layer1=array_layer1; array_layer2.push($limitPoint2); array_layer2.push($limitPoint1); array_layer2.push($tmp_point1); array_layer2.push($tmp_point2); bookArray_layer2=array_layer2; } private function DrawShape(shape:Shape,point_array:Array,myBmp:BitmapData,matr:Matrix):void { var num=point_array.length; shape.graphics.clear(); shape.graphics.beginBitmapFill(myBmp,matr,false,true); shape.graphics.moveTo(point_array[0].x,point_array[0].y); for (var i=1; i1) { u1=myXML.child("page")[i-2].attribute("src"); u2=myXML.child("page")[i-1].attribute("src"); if (u1==u2) { book_root["pload_" + i].brotherMC=book_root["pload_" + (i-1)]; book_root["pload_" + i].isWidthPage=true; } } book_root["pload_" + i]["loader"]=new Loader(); book_root["pload_" + i]["loader"].contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); book_root["pload_" + i]["loader"].contentLoaderInfo.addEventListener(Event.COMPLETE, LoadEnd); book_root["pload_" + i]["loader"].load(pageRequest); book_root["pload_" + i].addChild(book_root["pload_" + i]["loader"]); onLoadinit!=null?onLoadinit(book_root["pload_" + i]):null; } } private function SetPageMC(pageNum:Number):void { var p_mc1:MovieClip=new MovieClip(); var p_mc2:MovieClip=new MovieClip(); var MC_content1:MovieClip; var MC_content2:MovieClip; if (pageNum>0&&pageNum<=book_totalpage) { p_mc1=book_root["pload_"+pageNum]; } if ((pageNum+1)>0&&(pageNum+1)<=book_totalpage) { p_mc2=book_root["pload_"+(pageNum+1)]; } if (p_mc2.isWidthPage) { pageMC.addChild(p_mc1); p_mc1.x=p_mc1.y=0; if (p_mc1.loadedflag==true&&p_mc1.loadedtype=="application/x-shockwave-flash") { MC_content1=p_mc1["loader"].content; MC_content1.gotoAndPlay(2); } } else { pageMC.addChild(p_mc1); pageMC.addChild(p_mc2); p_mc1.x=p_mc1.y=0; p_mc2.x=book_width; p_mc2.y=0; if (p_mc1.loadedflag==true&&p_mc1.loadedtype=="application/x-shockwave-flash") { MC_content1=p_mc1["loader"].content; MC_content1.gotoAndPlay(2); } if (p_mc2.loadedflag==true&&p_mc2.loadedtype=="application/x-shockwave-flash") { MC_content2=p_mc2["loader"].content; MC_content2.gotoAndPlay(2); } } } //End Setting------------------------------------------------------------------------ //**Loder Parts------------------------------------------------------------------------ private function LoadFindLoader(LoaderObj):Number { var i:Number; var tmpageMC:MovieClip; for (i = 1; i<=book_totalpage; i++) { tmpageMC=book_root["pload_" + i]; if (tmpageMC["loader"].contentLoaderInfo==LoaderObj) { return i; } } return 0; } private function loadProgress(evtObj:ProgressEvent):void { var obj=evtObj.currentTarget; var n:Number=(LoadFindLoader(obj)); var percentLoaded:Number = evtObj.bytesLoaded/evtObj.bytesTotal; percentLoaded = Math.round(percentLoaded * 100); if (onLoading!=null) { onLoading(book_root["pload_" + n],percentLoaded); } } private function LoadEnd(evtObj:Event):void { var obj=evtObj.target.loader.parent; var n:Number=obj.id; var tmpPageMC:MovieClip; obj.loadedtype=evtObj.target.contentType; obj.loadedflag=true; if (obj.loadedtype=="application/x-shockwave-flash") { tmpPageMC=obj["loader"].content; if (obj.parent==null) { tmpPageMC.gotoAndStop(1); } else { tmpPageMC.gotoAndPlay(2); } } evtObj.target.loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, loadProgress); evtObj.target.loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, LoadEnd); if (onLoadEnd!=null) { onLoadEnd(obj); } } //Loder End--------------------------------------------------------------------------------- //**MouseEvent Parts------------------------------------------------------------------------ private function MouseOnDown(evt:Event):void { if (book_TimerFlag!="stop"||evt.target.hasEventListener(MouseEvent.CLICK)) { //不处于静止状态 return; } //mouseOnDown时取area绝对值; book_TimerArg0=MouseFindArea(new Point(book_root.mouseX,book_root.mouseY)); book_TimerArg0=book_TimerArg0<0?-book_TimerArg0:book_TimerArg0; if (book_TimerArg0==0) { //不在area区域 return; } else if ((book_TimerArg0==1||book_TimerArg0==2)&&book_page<=1) { //向左翻到顶 return; } else if ((book_TimerArg0==3||book_TimerArg0==4)&&book_page>=book_totalpage) { //向右翻到顶 return; } else { book_TimerFlag="startplay"; PageUp(); } } private function MouseOnUp(evt:Event):void { if (book_TimerFlag=="startplay") { //处于mousedown状态时 book_TimerArg1=MouseFindArea(new Point(book_root.mouseX,book_root.mouseY)); book_TimerFlag="autoplay"; } } private function MouseFindArea(point:Point):Number { /* 取下面的四个区域,返回数值: * -------------------- * | -1| | | -3 | * |--- | ----| * | 1 | 3 | * |---------|----------| * | 2 | 4 | * |---- | ----| * | -2 | | | -4 | * -------------------- */ var tmpn:Number; var minx:Number=0; var maxx:Number=book_width+book_width; var miny:Number=0; var maxy:Number=book_height; var areaNum:Number=50; if (point.x>minx&&point.x<=maxx*0.5) { tmpn=(point.y>miny&&point.y<=(maxy*0.5))?1:(point.y>(maxy*0.5)&&point.yminy&&point.y<=(miny+areaNum))?-1:(point.y>(maxy-areaNum)&&point.y(maxx*0.5)&&point.xminy&&point.y<=(maxy*0.5))?3:(point.y>(maxy*0.5)&&point.y=(maxx-areaNum)) { tmpn=(point.y>miny&&point.y<=(miny+areaNum))?-3:(point.y>(maxy-areaNum)&&point.y=0&&topage<=book_totalpage&&n!=0) { book_TimerArg0=n<0?1:3; book_TimerArg1=-1; book_topage=topage>book_totalpage?book_totalpage:topage; book_TimerFlag="autoplay"; PageUp(); } } public function PageDraw(pageNum:Number):BitmapData { var myBmp:BitmapData=new BitmapData(book_width, book_height,true, 0x000000); if (pageNum>0&&pageNum<=book_totalpage) { if (book_root["pload_"+pageNum].isWidthPage) { myBmp.draw(book_root["pload_"+pageNum].brotherMC, new Matrix(1,0,0,1,-book_width,0)); } else { myBmp.draw(book_root["pload_"+pageNum]); } } return myBmp; // } private function PageUp():void { var page1:Number; var page2:Number; var page3:Number; var page4:Number; var point_mypos:Point=book_myposArray[book_TimerArg0-1]; var b0:Bitmap; var b1:Bitmap; if (book_TimerArg0==1||book_TimerArg0==2) { book_topage=book_topage==book_page?book_page-2:book_topage; page1=book_page; page2=book_topage+1; page3=book_topage; page4=book_page+1; } else if (book_TimerArg0==3||book_TimerArg0==4) { book_topage=book_topage==book_page?book_page+2:book_topage; page1=book_page+1; page2=book_topage; page3=book_page; page4=book_topage+1; } book_px=point_mypos.x; book_py=point_mypos.y; Bmp0=PageDraw(page1); Bmp1=PageDraw(page2); bgBmp0=PageDraw(page3); bgBmp1=PageDraw(page4); b0 = new Bitmap(bgBmp0); b1 = new Bitmap(bgBmp1); b1.x=book_width; bgMC.addChild(b0); bgMC.addChild(b1); bgMC.visible=false; book_timer.start(); } //End Page------------------------------------------------------------------------ //**Timer Parts------------------------------------------------------------------------ private function bookTimerHandler(event:TimerEvent):void { var point_topos:Point=book_toposArray[book_TimerArg0-1]; var point_mypos:Point=book_myposArray[book_TimerArg0-1]; var PageObj:Object; var array_point1:Array; var array_point2:Array; var numpoint1:Number; var numpoint2:Number; var tmpMC0:MovieClip; var tmpageMC0:MovieClip; var tox:Number; var toy:Number; var toflag:Number; var tmpx:Number; var tmpy:Number; var arg:Number; var r:Number; var a:Number; bgMC.visible=true; while (pageMC.numChildren>0) { pageMC.removeChildAt(0); if (book_page>0&&book_page<=book_totalpage) { tmpMC0=book_root["pload_"+book_page]; if (tmpMC0.loadedflag==true&&tmpMC0.loadedtype=="application/x-shockwave-flash") { tmpageMC0=tmpMC0["loader"].content; tmpageMC0.gotoAndStop(1); } } if ((book_page+1)>0&&(book_page+1)<=book_totalpage) { tmpMC0=book_root["pload_"+(book_page+1)]; if (tmpMC0.loadedflag==true&&tmpMC0.loadedtype=="application/x-shockwave-flash") { tmpageMC0=tmpMC0["loader"].content; tmpageMC0.gotoAndStop(1); } } } if (book_TimerFlag=="startplay") { u=0.4; render0.graphics.clear(); render1.graphics.clear(); book_px=((render0.mouseX-book_px)*u+book_px)>>0; book_py=((render0.mouseY-book_py)*u+book_py)>>0; DrawPage(book_TimerArg0,new Point(book_px,book_py),Bmp1,Bmp0); //book_timer.stop(); } else if (book_TimerFlag=="autoplay") { render0.graphics.clear(); render1.graphics.clear(); if (Math.abs(point_topos.x-book_px)>book_width&&book_TimerArg1>0) { //不处于点翻区域并且翻页不过中线时 tox=point_mypos.x; toy=point_mypos.y; toflag=0; } else { tox=point_topos.x; toy=point_topos.y; toflag=1; } tmpx=(tox-book_px)>>0; tmpy=(toy-book_py)>>0; if (book_TimerArg1<0) { //处于点翻区域时 u=0.3;//降低加速度 book_py=Arc(book_width,tmpx,point_topos.y); } else { u=0.4;//原始加速度 book_py=tmpy*u+book_py; } book_px=tmpx*u+book_px; DrawPage(book_TimerArg0,new Point(book_px,book_py),Bmp1,Bmp0); if (tmpx==0&&tmpy==0) { render0.graphics.clear(); render1.graphics.clear(); shadow0.graphics.clear(); shadow1.graphics.clear(); Bmp0.dispose(); Bmp1.dispose(); bgBmp0.dispose(); bgBmp1.dispose(); while (bgMC.numChildren>0) { bgMC.removeChildAt(0); } book_topage=toflag==0?book_page:book_topage; book_page=book_topage; SetPageMC(book_page); book_TimerFlag="stop";//恢得静止状态 if (onPageEnd!=null) { onPageEnd(); } bgMC.visible=false; book_timer.reset(); } } } //End Timer ------------------------------------------------------------------------ //**Tools Parts------------------------------------------------------------------------ private function Arc(arg_R,arg_N1,arg_N2):Number { //------圆弧算法----------------------- var arg=arg_R*2; var r=arg_R*arg_R+arg*arg; var a=Math.abs(arg_N1)-arg_R; var R_arg:Number=arg_N2 - (Math.sqrt(r-a*a)-arg); return R_arg; } private function angle(target1,target2):Number { var tmp_x:Number=target1.x-target2.x; var tmp_y:Number=target1.y-target2.y; var tmp_angle:Number= Math.atan2(tmp_y, tmp_x)*180/Math.PI; tmp_angle = tmp_angle<0 ? tmp_angle+360 : tmp_angle; return tmp_angle; } private function pos(target1,target2):Number { var tmp_x:Number=target1.x-target2.x; var tmp_y:Number=target1.y-target2.y; var tmp_s:Number=Math.sqrt(tmp_x*tmp_x+tmp_y*tmp_y); return target1.x > target2.x?tmp_s:- tmp_s; } //End Tools------------------------------------------------------------------------ } }