|
@@ -0,0 +1,494 @@
|
|
|
|
+import Element from './runtime/element'
|
|
|
|
+const screenWidth = window.innerWidth
|
|
|
|
+const screenHeight = window.innerHeight
|
|
|
|
+import BackGround from './runtime/background'
|
|
|
|
+import Login from './runtime/login'
|
|
|
|
+import GameInfo from './runtime/gameinfo'
|
|
|
|
+import Music from './runtime/music'
|
|
|
|
+let ctx = canvas.getContext('2d')
|
|
|
|
+const scalx = screenWidth / 375
|
|
|
|
+const scaly = screenHeight / 667
|
|
|
|
+const numberY = 6; //行
|
|
|
|
+const numberX = 6; //
|
|
|
|
+const imgW = 47.5 * scalx;
|
|
|
|
+const imgH = 46.5 * scaly;
|
|
|
|
+const ceilX = 45 * scalx;
|
|
|
|
+const ceilY = 128 * scaly;
|
|
|
|
+const fixedW = 95;
|
|
|
|
+const animal = [0, 1, 2, 3, 4]; //元素种类
|
|
|
|
+const BG_IMG_SRC = 'images/bg.jpg'
|
|
|
|
+const BG_IMG_SRC2 = 'images/play.png'
|
|
|
|
+let minute = 0; //控制动画速度
|
|
|
|
+
|
|
|
|
+export default class App {
|
|
|
|
+ constructor() {
|
|
|
|
+ this.aniId = 0
|
|
|
|
+ this.boxData = []; // 消消乐数据
|
|
|
|
+ this.startCoordinates = [0, 0]; // 开始的坐标位置
|
|
|
|
+ this.endCoordinates = [0, 0]; // 结束的坐标位置
|
|
|
|
+ this.clickbox = []; // 点击的方块位置
|
|
|
|
+ this.click = 0;
|
|
|
|
+ this.isSwaping = false;
|
|
|
|
+ this.isClear = false;
|
|
|
|
+ this.boxArray = {}
|
|
|
|
+ this.speed = 0;
|
|
|
|
+ this.anB = 0;
|
|
|
|
+ this.maxx = 0;
|
|
|
|
+ this.repeat = false;
|
|
|
|
+ this.score = {
|
|
|
|
+ num: 0,
|
|
|
|
+ animal: 0
|
|
|
|
+ }
|
|
|
|
+ this.hoverImage = {
|
|
|
|
+ animal: 0,
|
|
|
|
+ x: 0,
|
|
|
|
+ y: 0
|
|
|
|
+ };
|
|
|
|
+ this.hoverSpeed = 0;
|
|
|
|
+ this.timer = 0;
|
|
|
|
+ this.fill = false;
|
|
|
|
+ this.w = 0;
|
|
|
|
+ this.draw = false;
|
|
|
|
+ this.restart();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ restart() {
|
|
|
|
+ this.login = new Login()
|
|
|
|
+ this.bindLoop = this.loop.bind(this)
|
|
|
|
+ this.bg = new BackGround(ctx, BG_IMG_SRC);
|
|
|
|
+ this.bg2 = new BackGround(ctx, BG_IMG_SRC2);
|
|
|
|
+ this.ele = new Element(ctx);
|
|
|
|
+ this.gameinfo = new GameInfo(ctx);
|
|
|
|
+ this.bindLoop = this.loop.bind(this);
|
|
|
|
+ this.music = new Music();
|
|
|
|
+ canvas.removeEventListener(
|
|
|
|
+ 'touchstart',
|
|
|
|
+ this.touchHandler
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ this.aniId = window.requestAnimationFrame(
|
|
|
|
+ this.bindLoop,
|
|
|
|
+ canvas
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ step1() {
|
|
|
|
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
|
|
+ this.bg.render(ctx);
|
|
|
|
+ this.login.render(ctx);
|
|
|
|
+ this.touchHandler = this.touchEventHandler.bind(this)
|
|
|
|
+ canvas.addEventListener('touchstart', this.touchHandler)
|
|
|
|
+ }
|
|
|
|
+ touchEventHandler(e) {
|
|
|
|
+ e.preventDefault()
|
|
|
|
+ let x = e.touches[0].clientX
|
|
|
|
+ let y = e.touches[0].clientY
|
|
|
|
+
|
|
|
|
+ let area = this.login.btnArea
|
|
|
|
+
|
|
|
|
+ if (x >= area.startX &&
|
|
|
|
+ x <= area.endX &&
|
|
|
|
+ y >= area.startY &&
|
|
|
|
+ y <= area.endY) {
|
|
|
|
+ this.secenceChange()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ secenceChange() {
|
|
|
|
+ canvas.removeEventListener(
|
|
|
|
+ 'touchstart',
|
|
|
|
+ this.touchHandler
|
|
|
|
+ )
|
|
|
|
+ this.boxArray = {}
|
|
|
|
+ window.cancelAnimationFrame(this.aniId);
|
|
|
|
+ console.log(this.aniId)
|
|
|
|
+ this.up = this.update.bind(this)
|
|
|
|
+ this.bgupdate()
|
|
|
|
+ this.aniId = window.requestAnimationFrame(this.up, canvas)
|
|
|
|
+ this.music.secen2()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ initEvent() {
|
|
|
|
+ canvas.addEventListener('touchstart', this.touchHandlerStart.bind(this))
|
|
|
|
+ canvas.addEventListener('touchmove', this.touchHanderMove.bind(this))
|
|
|
|
+ canvas.addEventListener('touchend', this.touchEnd.bind(this))
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ touchHandlerStart(e) {
|
|
|
|
+ if (this.fill) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ e.preventDefault()
|
|
|
|
+ this.isSwaping = false
|
|
|
|
+ this.repeat = false;
|
|
|
|
+ this.boxArray = {}
|
|
|
|
+ let x = e.touches[0].clientX - ceilX
|
|
|
|
+ let y = e.touches[0].clientY - ceilY
|
|
|
|
+ this.draw = false
|
|
|
|
+ if (x > 0 && y > 0 && x < (imgW * numberX + ceilX) && y < imgH * numberY + ceilY) {
|
|
|
|
+ this.startCoordinates = [x, y];
|
|
|
|
+ this.clickbox[0] = Math.ceil(x / imgW) - 1
|
|
|
|
+ this.clickbox[1] = Math.ceil(y / imgH) - 1
|
|
|
|
+ let col = this.clickbox[1]
|
|
|
|
+ let row = this.clickbox[0]
|
|
|
|
+ this.hoverImage = {
|
|
|
|
+ animal: this.boxData[row][col].animal,
|
|
|
|
+ x: row,
|
|
|
|
+ y: col
|
|
|
|
+ }
|
|
|
|
+ console.log("start1")
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ this.hover();
|
|
|
|
+ this.date1 = new Date();
|
|
|
|
+ }, 0);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //点击重玩区域
|
|
|
|
+ this.againPalyArea(e)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ hover() {
|
|
|
|
+ this.bgupdate();
|
|
|
|
+ this.boxData.forEach((item, x) => {
|
|
|
|
+ item.forEach((it, y) => {
|
|
|
|
+ this.ele.render(ctx, it['animal'] * fixedW, 0, imgW * x + ceilX, imgH * y + ceilY);
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+ this.ele.renderHover(ctx, this.hoverImage.animal * 95, 0, imgW * this.hoverImage.x + ceilX, imgH * this.hoverImage.y + ceilY);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ againPalyArea(e) {
|
|
|
|
+ let x = [],
|
|
|
|
+ y = [];
|
|
|
|
+ x[0] = 214 * scalx;
|
|
|
|
+ x[1] = 214 * scalx + 100 * scalx;
|
|
|
|
+ y[0] = screenHeight - 107 * scaly;
|
|
|
|
+ y[1] = screenHeight - 36 * scaly;
|
|
|
|
+ //点击重玩区域
|
|
|
|
+ if (e.touches[0].clientX > x[0] && e.touches[0].clientY > y[0] && e.touches[0].clientX < x[1] && e.touches[0].clientY < y[1]) {
|
|
|
|
+ this.secenceChange()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ //交换
|
|
|
|
+ touchHanderMove(e) {
|
|
|
|
+ this.date2 = new Date();
|
|
|
|
+ console.log((this.date2 - this.date1) * 3600 / 24 * 60 * 60, "start2")
|
|
|
|
+ e.preventDefault()
|
|
|
|
+ let x = e.touches[0].clientX - ceilX
|
|
|
|
+ let y = e.touches[0].clientY - ceilY
|
|
|
|
+ this.endCoordinates = [x, y];
|
|
|
|
+ //点击 交换区域
|
|
|
|
+ if (x > 0 && y > 0 && x < (ceilX + imgW * numberX) && y < ceilY + imgH * numberY) {
|
|
|
|
+ this.swap() //交换位置
|
|
|
|
+ this.isReapet();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (this.repeat) {
|
|
|
|
+
|
|
|
|
+ this.isSwaping = true;
|
|
|
|
+ if (this.draw) return
|
|
|
|
+ this.music.playswapAudio()
|
|
|
|
+ this.drawBlock();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ touchEnd(e) {
|
|
|
|
+ window.requestAnimationFrame(() => {
|
|
|
|
+ if (this.isSwaping) {
|
|
|
|
+ this.clear();
|
|
|
|
+ this.down();
|
|
|
|
+ this.fillBlock();
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }, canvas)
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ //
|
|
|
|
+ drawBlock() {
|
|
|
|
+ this.bgupdate();
|
|
|
|
+ this.draw = true;
|
|
|
|
+
|
|
|
|
+ this.boxData.forEach((item, x) => {
|
|
|
|
+ item.forEach((it, y) => {
|
|
|
|
+ this.ele.render(ctx, it['animal'] * fixedW, 0, imgW * x + ceilX, imgH * y + ceilY);
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //swap 二二交换
|
|
|
|
+ swap() {
|
|
|
|
+ const moveX = this.endCoordinates[0] - this.startCoordinates[0];
|
|
|
|
+ const moveY = this.endCoordinates[1] - this.startCoordinates[1];
|
|
|
|
+ const col = this.clickbox[0]; //y
|
|
|
|
+ const row = this.clickbox[1]; //x
|
|
|
|
+ if (Math.abs(moveX) > Math.abs(moveY) && moveX > 0) { // 从左到右
|
|
|
|
+ let tmp = this.boxData[col + 1][row];
|
|
|
|
+ this.boxData[col + 1][row] = this.boxData[col][row];
|
|
|
|
+ this.boxData[col][row] = tmp;
|
|
|
|
+ } else if (Math.abs(moveX) > Math.abs(moveY) && moveX < 0) { // 从右到左
|
|
|
|
+ let tmp = this.boxData[col - 1][row]
|
|
|
|
+ this.boxData[col - 1][row] = this.boxData[col][row];
|
|
|
|
+ this.boxData[col][row] = tmp;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ } else if (Math.abs(moveY) > Math.abs(moveX) && moveY > 0) { // 从上到下
|
|
|
|
+ let tmp = this.boxData[col][row + 1]
|
|
|
|
+ this.boxData[col][row + 1] = this.boxData[col][row];
|
|
|
|
+ this.boxData[col][row] = tmp;
|
|
|
|
+
|
|
|
|
+ } else if (Math.abs(moveY) > Math.abs(moveX) && moveY < 0) { // 从下到上
|
|
|
|
+ let tmp = this.boxData[col][row - 1]
|
|
|
|
+ this.boxData[col][row - 1] = this.boxData[col][row];
|
|
|
|
+ this.boxData[col][row] = tmp;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ bgupdate() {
|
|
|
|
+ ctx.clearRect(0, 0, screenWidth, screenHeight);
|
|
|
|
+ this.bg2.render(ctx, 0, 0, 2, 2);
|
|
|
|
+ this.gameinfo.renderGameScore(ctx)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ downgame() {
|
|
|
|
+ this.bgupdate();
|
|
|
|
+
|
|
|
|
+ this.boxData.forEach((item, x) => {
|
|
|
|
+ item.forEach((it, y) => {
|
|
|
|
+ if (this.boxData[x][y]['match'] > 0) {
|
|
|
|
+ // this.ele.render(ctx,it['animal']*fixedW,0,imgW*x+ceilX,imgH*y+ceilY,true);
|
|
|
|
+ if (this.score.animal == it['animal']) {
|
|
|
|
+ this.score.num++;
|
|
|
|
+ }
|
|
|
|
+ let key = x + '' + y
|
|
|
|
+ this.boxArray[key] = {
|
|
|
|
+ x: imgW * x + ceilX,
|
|
|
|
+ y: ceilY + imgH * y,
|
|
|
|
+ animal: animal[Math.floor(Math.random() * 5)],
|
|
|
|
+ match: 0
|
|
|
|
+ }
|
|
|
|
+ this.maxx = this.max(y, this.maxx)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ this.ele.render(ctx, it['animal'] * fixedW, 0, imgW * x + ceilX, imgH * y + ceilY);
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+ this.gameinfo.renderImage(ctx, this.score.num)
|
|
|
|
+ console.log(this.score.num, "xxxxxxxxxxx")
|
|
|
|
+ }
|
|
|
|
+ //隐藏要消灭的元素
|
|
|
|
+ clear() {
|
|
|
|
+
|
|
|
|
+ this.bgupdate();
|
|
|
|
+ this.boxData.forEach((item, x) => {
|
|
|
|
+ item.forEach((it, y) => {
|
|
|
|
+ if (this.boxData[x][y]['match'] > 0) {
|
|
|
|
+ this.ele.render(ctx, it['animal'] * fixedW, 0, imgW * x + ceilX, imgH * y + ceilY, true);
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ this.ele.render(ctx, it['animal'] * fixedW, 0, imgW * x + ceilX, imgH * y + ceilY);
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ //检测是否重复
|
|
|
|
+ isReapet() {
|
|
|
|
+ this.boxData.forEach((item, x) => {
|
|
|
|
+ item.forEach((it, y) => {
|
|
|
|
+ let vb = x + 2 < numberX && this.boxData[x][y]['animal'] == this.boxData[x + 1][y]['animal'] && this.boxData[x + 1][y]['animal'] == this.boxData[x + 2][y]['animal'];
|
|
|
|
+ let vy = y + 2 < numberY && this.boxData[x][y]['animal'] == this.boxData[x][y + 2]['animal'] && this.boxData[x][y + 1]['animal'] == this.boxData[x][y + 2]['animal'];
|
|
|
|
+ if (vb) {
|
|
|
|
+ ++this.boxData[x][y].match;
|
|
|
|
+ ++this.boxData[x + 1][y].match;
|
|
|
|
+ ++this.boxData[x + 2][y].match;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ if (vy) {
|
|
|
|
+ ++this.boxData[x][y].match;
|
|
|
|
+ ++this.boxData[x][y + 1].match;
|
|
|
|
+ ++this.boxData[x][y + 2].match;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ if (vb || vy) {
|
|
|
|
+ this.repeat = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ //方块下落
|
|
|
|
+ down() {
|
|
|
|
+ for (let x = numberY - 1; x > -1; x--) { //行
|
|
|
|
+ for (let y = 0; y < numberX; y++) { //列
|
|
|
|
+ if (this.boxData[y][x].match) {
|
|
|
|
+ for (let k = x - 1; k > -1; k--) {
|
|
|
|
+ if (this.boxData[y][k].match == 0) {
|
|
|
|
+ let temp = this.boxData[y][k];
|
|
|
|
+ this.boxData[y][k] = this.boxData[y][x]
|
|
|
|
+ this.boxData[y][x] = temp;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ this.downgame();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fillBlock() {
|
|
|
|
+ this.fill = true;
|
|
|
|
+
|
|
|
|
+ for (let key in this.boxArray) {
|
|
|
|
+
|
|
|
|
+ this.ele.renderBoom(ctx, this.speed * 93, 0, this.boxArray[key].x, this.boxArray[key].y);
|
|
|
|
+ if (this.isauto) {
|
|
|
|
+ this.music.playisAudio()
|
|
|
|
+ } else {
|
|
|
|
+ this.music.playclearAudio()
|
|
|
|
+ }
|
|
|
|
+ this.ele.render(ctx, this.boxArray[key]['animal'] * fixedW, 0, this.boxArray[key].x, minute - parseInt((this.maxx + 1) * imgH) + this.boxArray[key].y)
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ this.loopBlock();
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ max(a, b) {
|
|
|
|
+ return a > b ? a : b
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ loopBlock() {
|
|
|
|
+ this.anB = window.requestAnimationFrame(() => {
|
|
|
|
+ minute += 4;
|
|
|
|
+
|
|
|
|
+ if (minute < parseInt((this.maxx + 1) * imgH)) {
|
|
|
|
+ this.clear()
|
|
|
|
+ ++this.speed
|
|
|
|
+
|
|
|
|
+ this.fillBlock()
|
|
|
|
+ } else {
|
|
|
|
+ window.cancelAnimationFrame(this.anB);
|
|
|
|
+ this.clear()
|
|
|
|
+ for (let key in this.boxArray) {
|
|
|
|
+ console.log(key, "key11")
|
|
|
|
+ let ma = this.boxArray[key]['animal'] * fixedW
|
|
|
|
+ this.ele.render(ctx, ma, 0, this.boxArray[key].x, this.boxArray[key].y)
|
|
|
|
+ let x = key.substring(0, 1);
|
|
|
|
+ let y = key.substring(1, 2);
|
|
|
|
+ this.boxData[x][y]['animal'] = this.boxArray[key]['animal']
|
|
|
|
+ this.boxData[x][y]['match'] = this.boxArray[key]['match']
|
|
|
|
+ }
|
|
|
|
+ minute = 0;
|
|
|
|
+ this.isauto = false
|
|
|
|
+ this.speed = 0;
|
|
|
|
+ this.repeat = false;
|
|
|
|
+ this.isReapet()
|
|
|
|
+ console.log(this.boxData, this.repeat, "下落")
|
|
|
|
+ if (this.repeat) {
|
|
|
|
+ this.boxArray = {}
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ this.clear()
|
|
|
|
+ this.down()
|
|
|
|
+ this.isauto = true;
|
|
|
|
+ this.fillBlock()
|
|
|
|
+ }, 1000)
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ this.fill = false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }, canvas)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ touchHanderEnd(e) {
|
|
|
|
+ e.preventDefault();
|
|
|
|
+ canvas.removeEventListener('touchstart', this.touchHandlerStart.bind(this))
|
|
|
|
+ canvas.removeEventListener('touchmove', this.touchHanderMove.bind(this))
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ update() {
|
|
|
|
+ this.enemyGenerate();
|
|
|
|
+ this.initEvent();
|
|
|
|
+ this.gameinfo.renderImage(ctx, this.score.num)
|
|
|
|
+ }
|
|
|
|
+ loop() {
|
|
|
|
+ this.step1();
|
|
|
|
+ this.aniId = window.requestAnimationFrame(this.bindLoop, canvas)
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * 随机方块生成逻辑
|
|
|
|
+ * 帧数取模定义成生成的频率
|
|
|
|
+ * 左边和左边的左边
|
|
|
|
+ * 下边和下边的下边
|
|
|
|
+ */
|
|
|
|
+ enemyGenerate() {
|
|
|
|
+ this.boxData = [];
|
|
|
|
+ for (let i = 0; i < numberX; i++) {
|
|
|
|
+ for (let j = 0; j < numberY; j++) {
|
|
|
|
+ if (j == 0) this.boxData[i] = new Array(numberY)
|
|
|
|
+ this.boxData[i][j] = {
|
|
|
|
+ animal: animal[Math.floor(Math.random() * 5)],
|
|
|
|
+ match: 0
|
|
|
|
+ }
|
|
|
|
+ while (i > 1 && this.boxData[i][j]['animal'] == this.boxData[i - 2][j]['animal'] || j > 1 && this.boxData[i][j]['animal'] == this.boxData[i][j - 2]['animal']) this.boxData[i][j]['animal'] = animal[Math.floor(Math.random() * 4)]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.boxData.forEach((item, x) => {
|
|
|
|
+ item.forEach((it, y) => {
|
|
|
|
+ this.ele.render(ctx, it['animal'] * fixedW, 0, imgW * x + ceilX, imgH * y + ceilY);
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|