<commit_before>var isNode = typeof module != 'undefined' && module.exports;

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(canvas) {
  if (isNode) {
    var FS = require('fs');
    var Fabric = require('fabric').fabric;
  } else {
    var FS = fs;
    var Fabric = fabric;
  }

  var colorValues = {
    'red': 'rgb(193,40,11)',
    'orange': 'rgb(355,144,36)',
    'yellow': 'rgb(243,224,14)',
    'green': 'rgb(22,220,129)',
    'blue': 'rgb(35,67,232)',
    'violet': 'rgb(220,84,183)',
    'black': 'rgb(0,0,0)'
  };

  var imageOffset;

  function clone(obj) {
    var newobj = {};
    for (property in obj) {
      if (typeof obj[property] == 'object') {
        newobj[property] = clone(property);
      } else {
        newobj[property] = obj[property];
      }
    }
    return newobj;
  }

  /**
   * Cycles through an object and changes all numeric fields to ints
   * where necessary. 'context' is used for exception reporting and can be
   * left unset upon invocation.
   */
  function cleanJSON(json, context) {
    if (!context)
      context = "root";

    for (property in json) {
      if (typeof(json[property]) == 'object') {
        cleanJSON(json[property], context + '.' + property);
      }
      else if (property == 'x' || property == 'y' ||
               property == 'width' || property == 'height' ||
               property == 'radius') {
        if (typeof(json[property]) == 'string') {
          json[property] = parseInt(json[property]);
          if (isNaN(json[property])) {
            var msg = "In '" + context + "': property '" + property + "' is not a number.";
            throw msg;
          }
        }
      }
    }
  }

  function applyBackground(json, canvas, callback) {
    FS.readFile(json['sourceFile'], function (err, blob) {
      if (err) throw err;

      var dimensions = json['dimensions'];
      var finalDimensions = json['finalDimensions'];
      img = {'width': dimensions['width'], 'height': dimensions['height'], 'src':blob};

      Fabric.Image.fromObject(img, function(fimg) {
        top = img.height/2 - imageOffset['y'];
        if (top % 1 != 0) {
          top -= 0.5;
        }
        left = img.width / 2 - imageOffset['x'];
        if (left % 1 != 0) {
          left -= 0.5;
        }

        canvas.add(fimg.set('top', top).set('left', left));

        applyMarkup(json, canvas, callback);
      });
    });
  }

  function applyMarkup(json, canvas, callback) {
    var size = json['dimensions'];

    for (instruction in json['instructions']) {
      switch (instruction) {
        case 'draw':
          json['instructions']['draw'].forEach(function (e) {
            for (shapeName in e) {
              shape = e[shapeName];
              shape['shapeName'] = shapeName;

              switch (shapeName) {
                case 'rectangle':
                  drawRectangle(json['finalDimensions']['width'],
                   canvas, shape, imageOffset);
                  break;
                case 'circle':
                  drawCircle(json['finalDimensions']['width'], canvas,
                   shape, imageOffset);
                  break;
                default:
                  console.error('Unsupported Shape: ' + shapeName);
              }
            }
          });
          break;
        case 'crop':
          //もう処理していたから無視する
          break;
        default:
          console.error('Unsupported Instruction: ' + instruction);
      }
    }

    writeCanvas(json, canvas, callback);
  }

  function writeCanvas(json, canvas, callback) {
    canvas.renderAll();
    var outstream = FS.createWriteStream(json['destinationFile']),
        stream = canvas.createJPEGStream({
          quality: 93
        });

    stream.on('data', function(chunk) {
      outstream.write(chunk);
    });
    stream.on('end', function () {
      callback(canvas);
    });
  }

  function drawRectangle(finalWidth, canvas, shape, imageOffset) {
    shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
    whiteStroke = 2;

    var rect = {
      shapeName: shape['shapeName'],
      left: shape['from']['x']-imageOffset['x'],
      top: shape['from']['y']-imageOffset['y'],
      width: shape['size']['width'],
      height: shape['size']['height'],
      rx: 1,
      ry: 1,
      strokeWidth: shape['stroke'],
      stroke: colorValues[shape['color']],
      fill: 'transparent'
    }

    //Fabric調整
    rect['top'] = rect['top'] + rect['height'] / 2 + rect['strokeWidth'] / 2;
    rect['left'] = rect['left'] + rect['width'] / 2 + rect['strokeWidth'] / 2;

    var rectBorder = clone(rect);
    rectBorder['width'] = rectBorder['width'] + rect['strokeWidth'] + whiteStroke;
    rectBorder['height'] = rectBorder['height'] + rect['strokeWidth'] + whiteStroke;
    rectBorder['rx'] = rect['strokeWidth'] - whiteStroke;
    rectBorder['ry'] = rect['strokeWidth'] - whiteStroke;
    rectBorder['strokeWidth'] = whiteStroke;
    rectBorder['stroke'] = 'white';

    var rectInline = clone(rect);
    rectInline['width'] = rectInline['width'] - rect['strokeWidth'] + whiteStroke;
    rectInline['height'] = rectInline['height'] - rect['strokeWidth'] + whiteStroke;
    rectInline['rx'] = Math.max(shape['stroke'] - rectBorder['rx'],4);
    rectInline['ry'] = Math.max(shape['stroke'] - rectBorder['ry'],4);
    rectInline['strokeWidth'] = whiteStroke;
    rectInline['stroke'] = 'white';

    if (shadows == true) {
      if (!isNode)
        throw "Frontend shadows not implemented";

      drawShadow(canvas, rect, shadowStep);
    }

    var fabricRect = new Fabric.Rect(rect),
        fabricBorder = new Fabric.Rect(rectBorder),
        fabricInline = new Fabric.Rect(rectInline);

    canvas.add(fabricRect);
    canvas.add(fabricBorder);
    canvas.add(fabricInline);
  }

  function drawCircle(finalWidth, canvas, shape, imageOffset) {
    shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
    whiteStroke = 2;

    var circle = {
      shapeName: shape['shapeName'],
      left: shape['from']['x']-imageOffset['x'],
      top: shape['from']['y']-imageOffset['y'],
      radius: shape['radius'],
      strokeWidth: shape['stroke'],
      stroke: colorValues[shape['color']],
      fill: 'transparent'
    };
    var circleBorder = clone(circle);
    circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
    circleBorder['strokeWidth'] = whiteStroke;
    circleBorder['stroke'] = 'white';

    var circleInline = clone(circleBorder);
    circleInline['radius'] = circle['radius'] - circle['strokeWidth'] / 2;

    if (shadows == true) {
      drawShadow(canvas, circle, shadowStep);
    }

    var fabricCircle = new Fabric.Circle(circle),
        fabricBorder = new Fabric.Circle(circleBorder),
        fabricInline = new Fabric.Circle(circleInline);

    canvas.add(fabricCircle);
    canvas.add(fabricBorder);
    canvas.add(fabricInline);
  }

  /**
   * Draw a fuzzy shadow for the shape given.
   * Take care to draw the shadow before the shape.
   */
  function drawShadow(canvas, shape, step) {
    if (step < 1) {
      step = 1;
    }
    var shadow = Cloner.clone(shape);
    if (step > shadow['strokeWidth']) {
      step = shadow['strokeWidth'];
    }

    var offsetX = 8;
    var offsetY = offsetX;

    var shadow = Cloner.clone(shape);
    shadow['left'] += offsetX;
    shadow['top'] += offsetY;
    shadow['rx'] = 5;
    shadow['ry'] = 5;
    shadow['stroke'] = 'rgba(0,0,0,0.5)';
    shadow['strokeWidth'] = shadow['strokeWidth'] / step;
    var stepWidth = shadow['strokeWidth'];

    //Empirically-derived pixel tweaks to line up shadow sizes with their parents
    var circleTweak = 0.6875;
    var rectangleTweak = 1.3125;

    //Adjust shadow outlines to outer edge, to work towards inside later.
    switch (shape['shapeName']) {
      case 'circle':
        shadow['radius'] += shape['strokeWidth'] * circleTweak;
        shadow['strokeWidth'] *= 2;
        break;
      case 'rectangle':
        shadow['width'] += shape['strokeWidth'] * rectangleTweak;
        shadow['height'] += shape['strokeWidth'] * rectangleTweak;
        shadow['strokeWidth'] *= 2;
        break;
      default:
        console.error('実装されていない機能：' + shape['shapeName']);
        return;
    }

    var alpha;
    for (var i = 0; i < step; ++i) {
      if (i < (step / 2)) {
        alpha = (((i + 1) * 2) / (step));
      }
      else if (i == step / 2) {
        //Math ain't working to my likings
        alpha = 1;
      }
      else {
        alpha = (((step - (i + 1)) * 2) / (step));
      }

      shadow['stroke'] = 'rgba(0,0,0,' + alpha + ')';

      switch (shape['shapeName']) {
        case 'circle':
          canvas.add(new Fabric.Circle(shadow));
          shadow['radius'] = shadow['radius'] - stepWidth * 2 * 0.8;
          break;
        case 'rectangle':
          canvas.add(new Fabric.Rect(shadow));
          shadow['width'] = shadow['width'] - stepWidth * 4 * 0.8;
          shadow['height'] = shadow['height'] - stepWidth * 4 * 0.8;
          break;
        default:
          console.error('実装されてない機能：' + shape['shapeName']);
          return;
      }
    }
  }

  return {
    processJSON: function processJSON(json, callback) {
      //Make sure not to render every addition on server end
      canvas.renderOnAddition = !isNode;

      cleanJSON(json);

      var imagePath = json['sourceFile'];

      var crop = json['instructions']['crop'];
      imageOffset = (typeof crop != "undefined") ?
       {'x':crop['from']['x'],
        'y':crop['from']['y']} :
       {'x':0,'y':0};

      applyBackground(json, canvas, callback);
    }
  };
}

if (isNode)
  module.exports = ImageMarkupBuilder;
else
  var createImageFromMarkup = ImageMarkupBuilder;
<commit_msg>Group draw objects when not in Node.js<commit_after>var isNode = typeof module != 'undefined' && module.exports;

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(canvas) {
  if (isNode) {
    var FS = require('fs');
    var Fabric = require('fabric').fabric;
  } else {
    var FS = fs;
    var Fabric = fabric;
  }

  var colorValues = {
    'red': 'rgb(193,40,11)',
    'orange': 'rgb(355,144,36)',
    'yellow': 'rgb(243,224,14)',
    'green': 'rgb(22,220,129)',
    'blue': 'rgb(35,67,232)',
    'violet': 'rgb(220,84,183)',
    'black': 'rgb(0,0,0)'
  };

  var imageOffset;

  function clone(obj) {
    var newobj = {};
    for (property in obj) {
      if (typeof obj[property] == 'object') {
        newobj[property] = clone(property);
      } else {
        newobj[property] = obj[property];
      }
    }
    return newobj;
  }

  /**
   * Cycles through an object and changes all numeric fields to ints
   * where necessary. 'context' is used for exception reporting and can be
   * left unset upon invocation.
   */
  function cleanJSON(json, context) {
    if (!context)
      context = "root";

    for (property in json) {
      if (typeof(json[property]) == 'object') {
        cleanJSON(json[property], context + '.' + property);
      }
      else if (property == 'x' || property == 'y' ||
               property == 'width' || property == 'height' ||
               property == 'radius') {
        if (typeof(json[property]) == 'string') {
          json[property] = parseInt(json[property]);
          if (isNaN(json[property])) {
            var msg = "In '" + context + "': property '" + property + "' is not a number.";
            throw msg;
          }
        }
      }
    }
  }

  function applyBackground(json, canvas, callback) {
    FS.readFile(json['sourceFile'], function (err, blob) {
      if (err) throw err;

      var dimensions = json['dimensions'];
      var finalDimensions = json['finalDimensions'];
      img = {'width': dimensions['width'], 'height': dimensions['height'], 'src':blob};

      Fabric.Image.fromObject(img, function(fimg) {
        top = img.height/2 - imageOffset['y'];
        if (top % 1 != 0) {
          top -= 0.5;
        }
        left = img.width / 2 - imageOffset['x'];
        if (left % 1 != 0) {
          left -= 0.5;
        }

        canvas.add(fimg.set('top', top).set('left', left));

        applyMarkup(json, canvas, callback);
      });
    });
  }

  function applyMarkup(json, canvas, callback) {
    var size = json['dimensions'];

    for (instruction in json['instructions']) {
      switch (instruction) {
        case 'draw':
          json['instructions']['draw'].forEach(function (e) {
            for (shapeName in e) {
              shape = e[shapeName];
              shape['shapeName'] = shapeName;

              switch (shapeName) {
                case 'rectangle':
                  drawRectangle(json['finalDimensions']['width'],
                   canvas, shape, imageOffset);
                  break;
                case 'circle':
                  drawCircle(json['finalDimensions']['width'], canvas,
                   shape, imageOffset);
                  break;
                default:
                  console.error('Unsupported Shape: ' + shapeName);
              }
            }
          });
          break;
        case 'crop':
          //もう処理していたから無視する
          break;
        default:
          console.error('Unsupported Instruction: ' + instruction);
      }
    }

    writeCanvas(json, canvas, callback);
  }

  function writeCanvas(json, canvas, callback) {
    canvas.renderAll();
    var outstream = FS.createWriteStream(json['destinationFile']),
        stream = canvas.createJPEGStream({
          quality: 93
        });

    stream.on('data', function(chunk) {
      outstream.write(chunk);
    });
    stream.on('end', function () {
      callback(canvas);
    });
  }

  function drawRectangle(finalWidth, canvas, shape, imageOffset) {
    shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
    whiteStroke = 2;

    var rect = {
      shapeName: shape['shapeName'],
      left: shape['from']['x']-imageOffset['x'],
      top: shape['from']['y']-imageOffset['y'],
      width: shape['size']['width'],
      height: shape['size']['height'],
      rx: 1,
      ry: 1,
      strokeWidth: shape['stroke'],
      stroke: colorValues[shape['color']],
      fill: 'transparent'
    }

    //Fabric調整
    rect['top'] = rect['top'] + rect['height'] / 2 + rect['strokeWidth'] / 2;
    rect['left'] = rect['left'] + rect['width'] / 2 + rect['strokeWidth'] / 2;

    var rectBorder = clone(rect);
    rectBorder['width'] = rectBorder['width'] + rect['strokeWidth'] + whiteStroke;
    rectBorder['height'] = rectBorder['height'] + rect['strokeWidth'] + whiteStroke;
    rectBorder['rx'] = rect['strokeWidth'] - whiteStroke;
    rectBorder['ry'] = rect['strokeWidth'] - whiteStroke;
    rectBorder['strokeWidth'] = whiteStroke;
    rectBorder['stroke'] = 'white';

    var rectInline = clone(rect);
    rectInline['width'] = rectInline['width'] - rect['strokeWidth'] + whiteStroke;
    rectInline['height'] = rectInline['height'] - rect['strokeWidth'] + whiteStroke;
    rectInline['rx'] = Math.max(shape['stroke'] - rectBorder['rx'],4);
    rectInline['ry'] = Math.max(shape['stroke'] - rectBorder['ry'],4);
    rectInline['strokeWidth'] = whiteStroke;
    rectInline['stroke'] = 'white';

    if (shadows == true) {
      if (!isNode)
        throw "Frontend shadows not implemented";

      drawShadow(canvas, rect, shadowStep);
    }

    var fabricRect = new Fabric.Rect(rect),
        fabricBorder = new Fabric.Rect(rectBorder),
        fabricInline = new Fabric.Rect(rectInline);

    if (isNode) {
      canvas.add(fabricRect);
      canvas.add(fabricBorder);
      canvas.add(fabricInline);
    } else {
      var group = new Fabric.Group([fabricRect, fabricBorder, fabricInline],
       {left: fabricRect['left'], top: fabricRect['top']});
      canvas.add(group);
    }
  }

  function drawCircle(finalWidth, canvas, shape, imageOffset) {
    shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
    whiteStroke = 2;

    var circle = {
      shapeName: shape['shapeName'],
      left: shape['from']['x']-imageOffset['x'],
      top: shape['from']['y']-imageOffset['y'],
      radius: shape['radius'],
      strokeWidth: shape['stroke'],
      stroke: colorValues[shape['color']],
      fill: 'transparent'
    };
    var circleBorder = clone(circle);
    circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
    circleBorder['strokeWidth'] = whiteStroke;
    circleBorder['stroke'] = 'white';

    var circleInline = clone(circleBorder);
    circleInline['radius'] = circle['radius'] - circle['strokeWidth'] / 2;

    if (shadows == true) {
      drawShadow(canvas, circle, shadowStep);
    }

    var fabricCircle = new Fabric.Circle(circle),
        fabricBorder = new Fabric.Circle(circleBorder),
        fabricInline = new Fabric.Circle(circleInline);

    if (isNode) {
      canvas.add(fabricCircle);
      canvas.add(fabricBorder);
      canvas.add(fabricInline);
    } else {
      var group = new Fabric.Group([fabricCircle, fabricBorder, fabricInline],
       {left: fabricCircle['left'], top: fabricCircle['top']});
    }
  }

  /**
   * Draw a fuzzy shadow for the shape given.
   * Take care to draw the shadow before the shape.
   */
  function drawShadow(canvas, shape, step) {
    if (step < 1) {
      step = 1;
    }
    var shadow = Cloner.clone(shape);
    if (step > shadow['strokeWidth']) {
      step = shadow['strokeWidth'];
    }

    var offsetX = 8;
    var offsetY = offsetX;

    var shadow = Cloner.clone(shape);
    shadow['left'] += offsetX;
    shadow['top'] += offsetY;
    shadow['rx'] = 5;
    shadow['ry'] = 5;
    shadow['stroke'] = 'rgba(0,0,0,0.5)';
    shadow['strokeWidth'] = shadow['strokeWidth'] / step;
    var stepWidth = shadow['strokeWidth'];

    //Empirically-derived pixel tweaks to line up shadow sizes with their parents
    var circleTweak = 0.6875;
    var rectangleTweak = 1.3125;

    //Adjust shadow outlines to outer edge, to work towards inside later.
    switch (shape['shapeName']) {
      case 'circle':
        shadow['radius'] += shape['strokeWidth'] * circleTweak;
        shadow['strokeWidth'] *= 2;
        break;
      case 'rectangle':
        shadow['width'] += shape['strokeWidth'] * rectangleTweak;
        shadow['height'] += shape['strokeWidth'] * rectangleTweak;
        shadow['strokeWidth'] *= 2;
        break;
      default:
        console.error('実装されていない機能：' + shape['shapeName']);
        return;
    }

    var alpha;
    for (var i = 0; i < step; ++i) {
      if (i < (step / 2)) {
        alpha = (((i + 1) * 2) / (step));
      }
      else if (i == step / 2) {
        //Math ain't working to my likings
        alpha = 1;
      }
      else {
        alpha = (((step - (i + 1)) * 2) / (step));
      }

      shadow['stroke'] = 'rgba(0,0,0,' + alpha + ')';

      switch (shape['shapeName']) {
        case 'circle':
          canvas.add(new Fabric.Circle(shadow));
          shadow['radius'] = shadow['radius'] - stepWidth * 2 * 0.8;
          break;
        case 'rectangle':
          canvas.add(new Fabric.Rect(shadow));
          shadow['width'] = shadow['width'] - stepWidth * 4 * 0.8;
          shadow['height'] = shadow['height'] - stepWidth * 4 * 0.8;
          break;
        default:
          console.error('実装されてない機能：' + shape['shapeName']);
          return;
      }
    }
  }

  return {
    processJSON: function processJSON(json, callback) {
      //Make sure not to render every addition on server end
      canvas.renderOnAddition = !isNode;

      cleanJSON(json);

      var imagePath = json['sourceFile'];

      var crop = json['instructions']['crop'];
      imageOffset = (typeof crop != "undefined") ?
       {'x':crop['from']['x'],
        'y':crop['from']['y']} :
       {'x':0,'y':0};

      applyBackground(json, canvas, callback);
    }
  };
}

if (isNode)
  module.exports = ImageMarkupBuilder;
else
  var createImageFromMarkup = ImageMarkupBuilder;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         TruyenFull downloader
// @namespace    https://baivong.github.io/
// @description  Tải truyện từ truyenfull.vn định dạng epub
// @version      2.1.1
// @icon         https://i.imgur.com/FQY8btq.png
// @author       Zzbaivong
// @license      MIT
// @include      http://truyenfull.vn/*
// @exclude      http://truyenfull.vn/
// @exclude      http://truyenfull.vn/*/chuong-*
// @exclude      http://truyenfull.vn/danh-sach/*
// @exclude      http://truyenfull.vn/the-loai/*
// @exclude      http://truyenfull.vn/tac-gia/*
// @exclude      http://truyenfull.vn/contact/
// @exclude      http://truyenfull.vn/tos/
// @exclude      http://truyenfull.vn/sitemap.xml
// @require      https://code.jquery.com/jquery-3.2.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.2/handlebars.min.js
// @require      https://greasyfork.org/scripts/20307-jszip-v2/code/jszip-v2.js?version=196156
// @require      https://greasyfork.org/scripts/29904-jszip-utils/code/jszip-utils.js?version=196137
// @require      https://greasyfork.org/scripts/18532-filesaver/code/FileSaver.js?version=164030
// @require      https://greasyfork.org/scripts/29905-js-epub-maker/code/js-epub-maker.js?version=196139
// @noframes
// @connect      self
// @supportURL   https://github.com/baivong/Userscript/issues
// @run-at       document-idle
// @grant        GM_xmlhttpRequest
// ==/UserScript==

/* global EpubMaker */
(function ($, window, document) {
    'use strict';

    /**
     * Hiển thị liên kết xem online cuối chương
     * [>]
     * @type {Boolean} true
     *                 false
     */
    var readOnline = true;

    function cleanHtml(str) {
        str = str.replace(/&nbsp\;/gm, ' ');
        str = str.replace(/<(br|hr|img)([^>]+)?>/gm, '<$1$2 />');
        return '
' + str + '
';
    }

    function downloadError(err) {
        $downloadStatus('danger');
        titleError.push(chapTitle);
        console.log(err);
    }

    function saveEbook() {
        if (endDownload) return;
        endDownload = true;

        if (titleError.length) {
            titleError = '
Các chương lỗi: ' + titleError.join(', ') + '
';
        } else {
            titleError = '';
        }
        beginEnd = '
Nội dung từ ' + begin + ' đến ' + end + '
';

        epubMaker.withSection(new EpubMaker.Section('note', 'note', {
            content: beginEnd + titleError + '

' + credits,
            title: 'Ghi chú'
        }, false, true));

        epubMaker.downloadEpub(function (epubZipContent, filename) {
            document.title = '[⇓] ' + ebookTitle;
            $win.off('beforeunload');
            $download.attr({
                href: window.URL.createObjectURL(epubZipContent),
                download: filename
            }).text('Tải xong').off('click');
            $downloadStatus('success');
        });
    }

    function getContent() {
        if (endDownload) return;
        chapId = chapList[count];

        GM_xmlhttpRequest({
            method: 'GET',
            url: pathname + chapId + '/',
            onload: function (response) {
                var $data = $(response.responseText),
                    $chapter = $data.find('.chapter-c'),
                    $notContent = $chapter.find('script, style, a'),
                    $referrer = $chapter.find('[style]').filter(function () {
                        return (this.style.fontSize === '1px' || this.style.fontSize === '0px' || this.style.color === 'white');
                    });

                if (endDownload) return;

                chapTitle = $data.find('.chapter-title').text().trim();

                if (!$chapter.length) {
                    downloadError('Missing content.');
                } else {
                    $downloadStatus('warning');

                    var $img = $chapter.find('img');
                    if ($img.length) $img.replaceWith(function () {
                        return '
Click để xem ảnh
';
                    });

                    if ($notContent.length) $notContent.remove();
                    if ($referrer.length) $referrer.remove();

                    epubMaker.withSection(new EpubMaker.Section('chapter', chapId, {
                        content: cleanHtml($chapter.html() + chapRef(referrer + chapId)),
                        title: chapTitle
                    }, true, false));

                    if (count === 0) begin = chapTitle;
                    end = chapTitle;

                    $download.html('Đang tải: ' + Math.floor((count / chapListSize) * 100) + '%');

                    count++;
                    document.title = '[' + count + '] ' + pageName;
                    if (count >= chapListSize) {
                        saveEbook();
                    } else {
                        getContent();
                    }
                }
            },
            onerror: function (err) {
                downloadError(err);
                saveEbook();
            }
        });
    }

    function downloadEbook() {
        var $ebookType = $('.info a[itemprop="genre"]');

        ebookTitle = $('h1').text().trim();
        ebookAuthor = $('.info a[itemprop="author"]').text().trim();
        ebookCover = $('.books img').attr('src');

        if ($ebookType.length) {
            $ebookType.each(function () {
                ebookType.push($(this).text().trim());
            });
            ebookType = ebookType.join(', ');
            ebookType = '
Thể loại: ' + ebookType + '
';
        } else {
            ebookType = '';
        }

        epubMaker = new EpubMaker()
            .withUuid('github.com/baivong/Userscript::truyenfull::' + pathname.slice(1, -1))
            .withTemplate('idpf-wasteland')
            .withAuthor(ebookAuthor)
            .withLanguage('vi')
            .withModificationDate(new Date)
            .withCover(ebookCover)
            .withTitle(ebookTitle);

        epubMaker.withSection(new EpubMaker.Section('introduction', 'info', {
            content: '
' + ebookTitle + '

Tác giả: ' + ebookAuthor + '
' + ebookType,
            title: 'Giới thiệu'
        }, false, true));

        epubMaker.withSection(new EpubMaker.Section('preamble', 'content', {
            content: cleanHtml($('.desc-text').html()),
            title: 'Nội dung'
        }, false, true));

        $win.on('beforeunload', function () {
            return 'Truyện đang được tải xuống...';
        });

        $download.one('click', function (e) {
            e.preventDefault();
            saveEbook();
        });

        getContent();
    }

    var pageName = document.title,
        $win = $(window),

        $download = $('', {
            class: 'btn btn-primary btn-lg',
            href: '#download',
            text: 'Tải xuống'
        }),
        $downloadStatus = function (status) {
            $download.removeClass('btn-primary btn-success btn-info btn-warning btn-danger').addClass('btn-' + status);
        },

        $novelId = $('#truyen-id'),
        chapList = [],
        chapListSize = 0,
        chapId = '',
        chapTitle = '',
        pathname = location.pathname,
        count = 0,
        begin = '',
        end = '',
        endDownload = false,

        ebookTitle = '',
        ebookAuthor = '',
        ebookCover = '',
        ebookType = [],
        beginEnd = '',
        titleError = [],
        referrer = location.origin + pathname,
        credits = '
Truyện được tải từ TruyenFull

Userscript được viết bởi: Zzbaivong
',
        chapRef = function (ref) {
            return readOnline ? '
[>]
' : '';
        },

        epubMaker;

    if (!$novelId.length) return;

    $download.insertAfter('.info');
    $download.one('click contextmenu', function (e) {
        e.preventDefault();
        document.title = '[...] Vui lòng chờ trong giây lát';

        $.when($.get('/ajax.php', {
            type: 'hash'
        })).done(function (res) {
            $.get('/ajax.php', {
                type: 'chapter_option',
                data: $novelId.val(),
                bnum: '',
                num: 1,
                hash: res
            }).done(function (data) {
                chapList = data.match(/(?:value\=")[^"]+(?=")/g).map(function (val) {
                    return val.slice(7);
                });

                if (e.type === 'contextmenu') {
                    var startFrom = prompt('Nhập ID chương truyện bắt đầu tải:', chapList[0]);
                    startFrom = chapList.indexOf(startFrom);
                    if (startFrom !== -1) chapList = chapList.slice(startFrom);
                }

                chapListSize = chapList.length;
                if (chapListSize > 0) downloadEbook();
            }).fail(function (jqXHR, textStatus) {
                downloadError(textStatus);
            });
        }).fail(function (jqXHR, textStatus) {
            downloadError(textStatus);
        });
    });

})(jQuery, window, document);
<commit_msg>Remove invalid characters<commit_after>// ==UserScript==
// @name         TruyenFull downloader
// @namespace    https://baivong.github.io/
// @description  Tải truyện từ truyenfull.vn định dạng epub
// @version      2.1.1
// @icon         https://i.imgur.com/FQY8btq.png
// @author       Zzbaivong
// @license      MIT
// @include      http://truyenfull.vn/*
// @exclude      http://truyenfull.vn/
// @exclude      http://truyenfull.vn/*/chuong-*
// @exclude      http://truyenfull.vn/danh-sach/*
// @exclude      http://truyenfull.vn/the-loai/*
// @exclude      http://truyenfull.vn/tac-gia/*
// @exclude      http://truyenfull.vn/contact/
// @exclude      http://truyenfull.vn/tos/
// @exclude      http://truyenfull.vn/sitemap.xml
// @require      https://code.jquery.com/jquery-3.2.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.2/handlebars.min.js
// @require      https://greasyfork.org/scripts/20307-jszip-v2/code/jszip-v2.js?version=196156
// @require      https://greasyfork.org/scripts/29904-jszip-utils/code/jszip-utils.js?version=196137
// @require      https://greasyfork.org/scripts/18532-filesaver/code/FileSaver.js?version=164030
// @require      https://greasyfork.org/scripts/29905-js-epub-maker/code/js-epub-maker.js?version=196139
// @noframes
// @connect      self
// @supportURL   https://github.com/baivong/Userscript/issues
// @run-at       document-idle
// @grant        GM_xmlhttpRequest
// ==/UserScript==

/* global EpubMaker */
(function ($, window, document) {
    'use strict';

    /**
     * Hiển thị liên kết xem online cuối chương
     * [>]
     * @type {Boolean} true
     *                 false
     */
    var readOnline = true;

    function cleanHtml(str) {
        str = str.replace(/&nbsp\;/gm, ' ');
        str = str.replace(/<(br|hr|img)([^>]+)?>/gm, '<$1$2 />');
        str = str.replace(/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]+/gm, '');
        return '
' + str + '
';
    }

    function downloadError(err) {
        $downloadStatus('danger');
        titleError.push(chapTitle);
        console.log(err);
    }

    function saveEbook() {
        if (endDownload) return;
        endDownload = true;

        if (titleError.length) {
            titleError = '
Các chương lỗi: ' + titleError.join(', ') + '
';
        } else {
            titleError = '';
        }
        beginEnd = '
Nội dung từ ' + begin + ' đến ' + end + '
';

        epubMaker.withSection(new EpubMaker.Section('note', 'note', {
            content: beginEnd + titleError + '

' + credits,
            title: 'Ghi chú'
        }, false, true));

        epubMaker.downloadEpub(function (epubZipContent, filename) {
            document.title = '[⇓] ' + ebookTitle;
            $win.off('beforeunload');
            $download.attr({
                href: window.URL.createObjectURL(epubZipContent),
                download: filename
            }).text('Tải xong').off('click');
            $downloadStatus('success');
        });
    }

    function getContent() {
        if (endDownload) return;
        chapId = chapList[count];

        GM_xmlhttpRequest({
            method: 'GET',
            url: pathname + chapId + '/',
            onload: function (response) {
                var $data = $(response.responseText),
                    $chapter = $data.find('.chapter-c'),
                    $notContent = $chapter.find('script, style, a'),
                    $referrer = $chapter.find('[style]').filter(function () {
                        return (this.style.fontSize === '1px' || this.style.fontSize === '0px' || this.style.color === 'white');
                    });

                if (endDownload) return;

                chapTitle = $data.find('.chapter-title').text().trim();

                if (!$chapter.length) {
                    downloadError('Missing content.');
                } else {
                    $downloadStatus('warning');

                    var $img = $chapter.find('img');
                    if ($img.length) $img.replaceWith(function () {
                        return '
Click để xem ảnh
';
                    });

                    if ($notContent.length) $notContent.remove();
                    if ($referrer.length) $referrer.remove();

                    epubMaker.withSection(new EpubMaker.Section('chapter', chapId, {
                        content: cleanHtml($chapter.html() + chapRef(referrer + chapId)),
                        title: chapTitle
                    }, true, false));

                    if (count === 0) begin = chapTitle;
                    end = chapTitle;

                    $download.html('Đang tải: ' + Math.floor((count / chapListSize) * 100) + '%');

                    count++;
                    document.title = '[' + count + '] ' + pageName;
                    if (count >= chapListSize) {
                        saveEbook();
                    } else {
                        getContent();
                    }
                }
            },
            onerror: function (err) {
                downloadError(err);
                saveEbook();
            }
        });
    }

    function downloadEbook() {
        var $ebookType = $('.info a[itemprop="genre"]');

        ebookTitle = $('h1').text().trim();
        ebookAuthor = $('.info a[itemprop="author"]').text().trim();
        ebookCover = $('.books img').attr('src');

        if ($ebookType.length) {
            $ebookType.each(function () {
                ebookType.push($(this).text().trim());
            });
            ebookType = ebookType.join(', ');
            ebookType = '
Thể loại: ' + ebookType + '
';
        } else {
            ebookType = '';
        }

        epubMaker = new EpubMaker()
            .withUuid('github.com/baivong/Userscript::truyenfull::' + pathname.slice(1, -1))
            .withTemplate('idpf-wasteland')
            .withAuthor(ebookAuthor)
            .withLanguage('vi')
            .withModificationDate(new Date)
            .withCover(ebookCover)
            .withTitle(ebookTitle);

        epubMaker.withSection(new EpubMaker.Section('introduction', 'info', {
            content: '
' + ebookTitle + '

Tác giả: ' + ebookAuthor + '
' + ebookType,
            title: 'Giới thiệu'
        }, false, true));

        epubMaker.withSection(new EpubMaker.Section('preamble', 'content', {
            content: cleanHtml($('.desc-text').html()),
            title: 'Nội dung'
        }, false, true));

        $win.on('beforeunload', function () {
            return 'Truyện đang được tải xuống...';
        });

        $download.one('click', function (e) {
            e.preventDefault();
            saveEbook();
        });

        getContent();
    }

    var pageName = document.title,
        $win = $(window),

        $download = $('', {
            class: 'btn btn-primary btn-lg',
            href: '#download',
            text: 'Tải xuống'
        }),
        $downloadStatus = function (status) {
            $download.removeClass('btn-primary btn-success btn-info btn-warning btn-danger').addClass('btn-' + status);
        },

        $novelId = $('#truyen-id'),
        chapList = [],
        chapListSize = 0,
        chapId = '',
        chapTitle = '',
        pathname = location.pathname,
        count = 0,
        begin = '',
        end = '',
        endDownload = false,

        ebookTitle = '',
        ebookAuthor = '',
        ebookCover = '',
        ebookType = [],
        beginEnd = '',
        titleError = [],
        referrer = location.origin + pathname,
        credits = '
Truyện được tải từ TruyenFull

Userscript được viết bởi: Zzbaivong
',
        chapRef = function (ref) {
            return readOnline ? '
[>]
' : '';
        },

        epubMaker;

    if (!$novelId.length) return;

    $download.insertAfter('.info');
    $download.one('click contextmenu', function (e) {
        e.preventDefault();
        document.title = '[...] Vui lòng chờ trong giây lát';

        $.when($.get('/ajax.php', {
            type: 'hash'
        })).done(function (res) {
            $.get('/ajax.php', {
                type: 'chapter_option',
                data: $novelId.val(),
                bnum: '',
                num: 1,
                hash: res
            }).done(function (data) {
                chapList = data.match(/(?:value\=")[^"]+(?=")/g).map(function (val) {
                    return val.slice(7);
                });

                if (e.type === 'contextmenu') {
                    var startFrom = prompt('Nhập ID chương truyện bắt đầu tải:', chapList[0]);
                    startFrom = chapList.indexOf(startFrom);
                    if (startFrom !== -1) chapList = chapList.slice(startFrom);
                }

                chapListSize = chapList.length;
                if (chapListSize > 0) downloadEbook();
            }).fail(function (jqXHR, textStatus) {
                downloadError(textStatus);
            });
        }).fail(function (jqXHR, textStatus) {
            downloadError(textStatus);
        });
    });

})(jQuery, window, document);
<|endoftext|>


<commit_before>dojo.provide("app.src");

dojo.require("dojo.fx");
dojo.require("dojox.fx.easing");
dojo.require("dojo.parser");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.TabContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Slider");
dojo.require("dojox.form.DropDownSelect");
dojo.require("dojox.grid.Grid");
dojo.require("org.ppwcode.dojo.dijit.form.PpwMasterView");
dojo.require("org.ppwcode.dojo.dojox.grid.data.model");
dojo.require("org.ppwcode.dojo.dijit.form.PpwObjectForm");
dojo.require("org.ppwcode.dojo.dijit.form.PpwCrudForm");
dojo.require("org.ppwcode.dojo.dijit.form.PpwViewFormControllerDwr");
dojo.require("org.ppwcode.dojo.dijit.form.PpwImageBox");

var fbfriendsgridview = {
        cells: [
          [
            {name: "First Name", field: "first_name", width: "8em"},
            {name: "Last Name", field: "last_name", width: "100%"}
          ]
        ]
};
var fbfriendsgridlayout = [ fbfriendsgridview ];
var fbfriendsgridmodel = new org.ppwcode.dojo.dojox.grid.data.PpwObjects();

var yourmovielistgridlayout = [
	{
		cells: [[
		         {name: "Title", field: "title", width:"100%"}
		        ]]
	}
];
var yourmovielistgridmodel = new org.ppwcode.dojo.dojox.grid.data.PpwObjects();

function formatRating(therating) {
	var result = "";
	var i = 0;
	for (i = 0; i < therating; i++) {
		result += "";
	}
	for (i = 0; i < 5 - therating; i++) {
		result += "";
	}
	return result;
}

function formatMediaType(value) {
	var mediaTypeMap = {
			OTHER: "Other",
			BLURAY: "Blu-Ray",
			DVD: "DVD",
			DIVX_XVID: "DivX/XviD"
	}
	return mediaTypeMap[value];
}

var fbfriendsmoviesgridlayout = [
  {
    cells: [[
      {name: "Title", field: "title", width:"30em"},
      {name: "Rating", field: "rating", width: "100px", formatter: formatRating},
      {name: "Media type", field: "mediaType", width: "6em", formatter: formatMediaType}
    ]]
  }
];

var fbfriendsmoviesgridmodel = new org.ppwcode.dojo.dojox.grid.data.PpwObjects();

var fbFriendFormMap = [
  { property: "pic", fieldid: "FbFriendPicture", isEditable: false},
  { property: "first_name", fieldid: "FbFriendFirstName", isEditable: false},
  { property: "last_name", fieldid: "FbFriendLastName", isEditable: false}
];

var yourMovieFormMap = [
  { property: "persistenceId", fieldid: "yourMovieIdHiddenBox", isId: true},
  { property: "persistenceVersion", fieldid: "yourMoviePersistenceVersionHiddenBox"},
  { property: "facebookUserId", fieldid: "yourFacebookUserIdHiddenBox"},
  { property: "title", fieldid: "yourMovieTitle"},
  { property: "mediaType", fieldid: "yourMovieMediaType" },
  { property: "rating", fieldid: "yourMovieRating" }
];

function retrieve_movies(fbuid) {
	console.log("getting movies for user " + fbuid);
	JpaMovieDaoWrapper.findByFacebookUser(fbuid,
		{
			callback: function(response) {
				console.log (response.length + " movies for user " + fbuid);
				lstYourMovies.setData(response);
			},
			errorHandler: function(errorString, exception) {
				console.error("could not get movies for user " + fbuid + ": " + errorString);
			}
		}
	);
}

var yourMovieViewFormController;

function initializeDwrControllers() {
	yourMovieViewFormController = new org.ppwcode.dojo.dijit.form.PpwViewFormControllerDwr();
	yourMovieViewFormController.dwrRetrieveFunction = JpaMovieDaoWrapper.findByFacebookUser;
    yourMovieViewFormController.dwrCreateFunction = JpaAtomicStatelessCrudDaoWrapper.createPersistentBean;
    yourMovieViewFormController.dwrUpdateFunction = JpaAtomicStatelessCrudDaoWrapper.updatePersistentBean;
    yourMovieViewFormController.configure(lstYourMovies, frmYourMovie);

    yourMovieViewFormController.beforeItemCreate = function(item) {
    	item.facebookUserId = FB.Connect.get_loggedInUser();
    }
}

function show_user() {
   var node = document.getElementById("userbox");
   node.innerHTML =
         "
"
       + "

"
       + "    "
       + "      <fb:profile-pic uid='loggedinuser' facebook-logo='true'></fb:profile-pic>"
       + "    	"
       + "      Welcome, <fb:name uid='loggedinuser' useyou='false'></fb:name>.  You are signed in with"
       + "      your facebook account."
       + "    	"
       + "

"
       + "    "
       + "      <button dojoType='dijit.form.Button' onclick='do_logout();'>logout</button>"
       + "    	"
       + "

"
       + "
"
   FB.XFBML.Host.parseDomTree();
   dojo.parser.parse(node);
}

function retrieve_friends() {
    FB.Facebook.apiClient.friends_get(null, function(result, ex) {
            FB.Facebook.apiClient.users_getInfo(result,
                                                ["pic", "first_name", "last_name"],
                                                function(result, ex) {
            fbfriendsgridmodel.setData(result);
        });
      });
}

function do_login() {
	console.log("doing login routine");
	show_user();
	retrieve_friends();
	yourMovieViewFormController.setDwrRetrieveFunctionParameters([FB.Connect.get_loggedInUser()]);
	yourMovieViewFormController.fillMasterView();
}

function do_logout() {
	console.log("doing logout routine");
    fbfriendsgridmodel.clear();
    fbFriendForm.reset();
    lstYourMovies.setData(null);
    fbfriendsmoviesgridmodel.setData(null);
    FB.Connect.logout(function() {
        var node = document.getElementById("userbox");
        node.innerHTML = "<fb:login-button onlogin='show_user();'></fb:login-button>";
        FB.XFBML.Host.parseDomTree();
    });
}

function showall() {
	setTimeout(function() {
			dojo.fadeOut({node: 'splashscreen', duration: 150}).play();
			//dojo.fx.wipeOut({node: 'frmYourMovie', duration: 300}).play();
			hideFrmYourMovie();
			dojo.fadeOut({node: 'fbFriendForm', duration: 300}).play();
			setTimeout(function() {dojo.query("#splashscreen").orphan();}, 250);
		},
		20
	);
}

function slideFrmYourMovie(amt) {
    var slideArgs = {
      node: "frmYourMovie",
      left: (amt).toString(),
      unit: "px"
    };
    dojo.fx.slideTo(slideArgs).play();
 }

function showFrmYourMovie() {
	slideFrmYourMovie(0);
}

function hideFrmYourMovie() {
	slideFrmYourMovie(-400);
}

function showFriendDetails(e) {
    var user = fbfriendsgridmodel.getRow(e.rowIndex);

	  var animation = new dojo._Animation({curve: [0, 1], duration: 1});
	  dojo.connect(animation, "onPlay", function() { fbFriendForm.displayItem(user) });

	  dojo.fx.chain([
		  dojo.fadeOut({node: fbFriendForm.id, duration: 250}),
		  animation,
	      dojo.fadeIn({node: fbFriendForm.id, duration: 250})
    ]).play();

function showLstFriendsMoviesGrid() {
/*
dojo.fx.chain([
dojo.fx.slideTo({ node: "lstFriendsMoviesGrid", top: (1000).toString(), unit: "px" }),
dojo.fx.slideTo({ node: "lstFriendsMoviesGrid", top: (0).toString(), unit: "px", duration: 1000, easing: dojox.fx.easing.elasticOut })
]).play();
*/
dojo.fx.chain([
dojo.fx.slideTo({ node: "lstFriendsMoviesGrid", left: (1500).toString(), unit: "px", duration: 500 }),
dojo.fx.slideTo({ node: "lstFriendsMoviesGrid", left: (0).toString(), unit: "px", duration: 1000, easing: dojox.fx.easing.elasticOut })
]).play();

}

	  //dojo.fadeIn({node: 'lstFriendsMoviesGrid', duration: 150}).play();
    showLstFriendsMoviesGrid();

    console.dir(user);
    JpaMovieDaoWrapper.findByFacebookUser(user.uid,
      {
        callback: function(response) {
          fbfriendsmoviesgridmodel.setData(response);
        },
        errorHandler: function(errorString, ex) {
          console.error(errorString);
        }
      }
    );

}

dojo.addOnLoad(function() {
	dojo.parser.parse();
	console.info("done parsing");
	fbFriendForm.setFormMap(fbFriendFormMap);
	frmYourMovie.setFormMap(yourMovieFormMap);
	initializeDwrControllers();

	dojo.connect(lstYourMovies,"onAddButtonClick", showFrmYourMovie);
	dojo.connect(frmYourMovie, "onCreateModeCancelButtonClick", hideFrmYourMovie);
	dojo.connect(lstYourMovies,"onGridRowClick", showFrmYourMovie);

	dojo.connect(fbFriendsGrid, "onRowClick", showFriendDetails);

	FB.ensureInit(function() {
		console.log("before ifUserConnected");
		FB.Connect.ifUserConnected(do_login, do_logout);
		console.log("after ifUserConnected");
		showall();
	});
});
<commit_msg>Reworked animations<commit_after>dojo.provide("app.src");

dojo.require("dojo.fx");
dojo.require("dojox.fx.easing");
dojo.require("dojo.parser");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.TabContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Slider");
dojo.require("dojox.form.DropDownSelect");
dojo.require("dojox.grid.Grid");
dojo.require("org.ppwcode.dojo.dijit.form.PpwMasterView");
dojo.require("org.ppwcode.dojo.dojox.grid.data.model");
dojo.require("org.ppwcode.dojo.dijit.form.PpwObjectForm");
dojo.require("org.ppwcode.dojo.dijit.form.PpwCrudForm");
dojo.require("org.ppwcode.dojo.dijit.form.PpwViewFormControllerDwr");
dojo.require("org.ppwcode.dojo.dijit.form.PpwImageBox");

var fbfriendsgridview = {
        cells: [
          [
            {name: "First Name", field: "first_name", width: "8em"},
            {name: "Last Name", field: "last_name", width: "100%"}
          ]
        ]
};
var fbfriendsgridlayout = [ fbfriendsgridview ];
var fbfriendsgridmodel = new org.ppwcode.dojo.dojox.grid.data.PpwObjects();

var yourmovielistgridlayout = [
	{
		cells: [[
		         {name: "Title", field: "title", width:"100%"}
		        ]]
	}
];
var yourmovielistgridmodel = new org.ppwcode.dojo.dojox.grid.data.PpwObjects();

function formatRating(therating) {
	var result = "";
	var i = 0;
	for (i = 0; i < therating; i++) {
		result += "";
	}
	for (i = 0; i < 5 - therating; i++) {
		result += "";
	}
	return result;
}

function formatMediaType(value) {
	var mediaTypeMap = {
			OTHER: "Other",
			BLURAY: "Blu-Ray",
			DVD: "DVD",
			DIVX_XVID: "DivX/XviD"
	}
	return mediaTypeMap[value];
}

var fbfriendsmoviesgridlayout = [
  {
    cells: [[
      {name: "Title", field: "title", width:"30em"},
      {name: "Rating", field: "rating", width: "100px", formatter: formatRating},
      {name: "Media type", field: "mediaType", width: "6em", formatter: formatMediaType}
    ]]
  }
];

var fbfriendsmoviesgridmodel = new org.ppwcode.dojo.dojox.grid.data.PpwObjects();

var fbFriendFormMap = [
  { property: "pic", fieldid: "FbFriendPicture", isEditable: false},
  { property: "first_name", fieldid: "FbFriendFirstName", isEditable: false},
  { property: "last_name", fieldid: "FbFriendLastName", isEditable: false}
];

var yourMovieFormMap = [
  { property: "persistenceId", fieldid: "yourMovieIdHiddenBox", isId: true},
  { property: "persistenceVersion", fieldid: "yourMoviePersistenceVersionHiddenBox"},
  { property: "facebookUserId", fieldid: "yourFacebookUserIdHiddenBox"},
  { property: "title", fieldid: "yourMovieTitle"},
  { property: "mediaType", fieldid: "yourMovieMediaType" },
  { property: "rating", fieldid: "yourMovieRating" }
];

function retrieve_movies(fbuid) {
	console.log("getting movies for user " + fbuid);
	JpaMovieDaoWrapper.findByFacebookUser(fbuid,
		{
			callback: function(response) {
				console.log (response.length + " movies for user " + fbuid);
				lstYourMovies.setData(response);
			},
			errorHandler: function(errorString, exception) {
				console.error("could not get movies for user " + fbuid + ": " + errorString);
			}
		}
	);
}

var yourMovieViewFormController;

function initializeDwrControllers() {
	yourMovieViewFormController = new org.ppwcode.dojo.dijit.form.PpwViewFormControllerDwr();
	yourMovieViewFormController.dwrRetrieveFunction = JpaMovieDaoWrapper.findByFacebookUser;
    yourMovieViewFormController.dwrCreateFunction = JpaAtomicStatelessCrudDaoWrapper.createPersistentBean;
    yourMovieViewFormController.dwrUpdateFunction = JpaAtomicStatelessCrudDaoWrapper.updatePersistentBean;
    yourMovieViewFormController.configure(lstYourMovies, frmYourMovie);

    yourMovieViewFormController.beforeItemCreate = function(item) {
    	item.facebookUserId = FB.Connect.get_loggedInUser();
    }
}

function show_user() {
   var node = document.getElementById("userbox");
   node.innerHTML =
         "
"
       + "

"
       + "    "
       + "      <fb:profile-pic uid='loggedinuser' facebook-logo='true'></fb:profile-pic>"
       + "    	"
       + "      Welcome, <fb:name uid='loggedinuser' useyou='false'></fb:name>.  You are signed in with"
       + "      your facebook account."
       + "    	"
       + "

"
       + "    "
       + "      <button dojoType='dijit.form.Button' onclick='do_logout();'>logout</button>"
       + "    	"
       + "

"
       + "
"
   FB.XFBML.Host.parseDomTree();
   dojo.parser.parse(node);
}

function retrieve_friends() {
    FB.Facebook.apiClient.friends_get(null, function(result, ex) {
            FB.Facebook.apiClient.users_getInfo(result,
                                                ["pic", "first_name", "last_name"],
                                                function(result, ex) {
            fbfriendsgridmodel.setData(result);
        });
      });
}

function do_login() {
	console.log("doing login routine");
	show_user();
	retrieve_friends();
	yourMovieViewFormController.setDwrRetrieveFunctionParameters([FB.Connect.get_loggedInUser()]);
	yourMovieViewFormController.fillMasterView();
}

function do_logout() {
	console.log("doing logout routine");
    fbfriendsgridmodel.clear();
    fbFriendForm.reset();
    lstYourMovies.setData(null);
    fbfriendsmoviesgridmodel.setData(null);
    FB.Connect.logout(function() {
        var node = document.getElementById("userbox");
        node.innerHTML = "<fb:login-button onlogin='show_user();'></fb:login-button>";
        FB.XFBML.Host.parseDomTree();
    });
}

function showall() {
	setTimeout(function() {
			dojo.fadeOut({node: 'splashscreen', duration: 150}).play();
			//dojo.fx.wipeOut({node: 'frmYourMovie', duration: 300}).play();
			hideFrmYourMovie();
			dojo.fadeOut({node: 'fbFriendForm', duration: 300}).play();
			setTimeout(function() {dojo.query("#splashscreen").orphan();}, 250);
		},
		20
	);
}

function slideFrmYourMovie(amt) {
    var slideArgs = {
      node: "frmYourMovie",
      left: (amt).toString(),
      unit: "px"
    };
    dojo.fx.slideTo(slideArgs).play();
 }

function showFrmYourMovie() {
	slideFrmYourMovie(0);
}

function hideFrmYourMovie() {
	slideFrmYourMovie(-400);
}

function showFriendDetails(e) {
    var user = fbfriendsgridmodel.getRow(e.rowIndex);

    dojo.fx.chain([
	      dojo.fx.combine([
	          dojo.fadeOut({node: fbFriendForm.id, duration: 250, onEnd: function() { fbFriendForm.displayItem(user) } }),
	          dojo.fx.slideTo({ node: "fbFriendContentPane", left: (1500).toString(), unit: "px", duration: 500 })
	      ]),
		  dojo.fx.combine([
              dojo.fadeIn({node: fbFriendForm.id, duration: 1500}),
              dojo.fx.slideTo({ node: "fbFriendContentPane", left: (0).toString(), unit: "px", duration: 1000, easing: dojox.fx.easing.bounceOut })
		  ])

	]).play();

/*
dojo.fx.chain([
dojo.fx.slideTo({ node: "lstFriendsMoviesGrid", top: (1000).toString(), unit: "px" }),
dojo.fx.slideTo({ node: "lstFriendsMoviesGrid", top: (0).toString(), unit: "px", duration: 1000, easing: dojox.fx.easing.elasticOut })
]).play();
*/
	  //dojo.fadeIn({node: 'lstFriendsMoviesGrid', duration: 150}).play();

    console.dir(user);
    JpaMovieDaoWrapper.findByFacebookUser(user.uid,
      {
        callback: function(response) {
          fbfriendsmoviesgridmodel.setData(response);
        },
        errorHandler: function(errorString, ex) {
          console.error(errorString);
        }
      }
    );

}

dojo.addOnLoad(function() {
	dojo.parser.parse();
	console.info("done parsing");
	fbFriendForm.setFormMap(fbFriendFormMap);
	frmYourMovie.setFormMap(yourMovieFormMap);
	initializeDwrControllers();

	dojo.connect(lstYourMovies,"onAddButtonClick", showFrmYourMovie);
	dojo.connect(frmYourMovie, "onCreateModeCancelButtonClick", hideFrmYourMovie);
	dojo.connect(lstYourMovies,"onGridRowClick", showFrmYourMovie);

	dojo.connect(fbFriendsGrid, "onRowClick", showFriendDetails);

	FB.ensureInit(function() {
		console.log("before ifUserConnected");
		FB.Connect.ifUserConnected(do_login, do_logout);
		console.log("after ifUserConnected");
		showall();
	});
});
<|endoftext|>


<commit_before>const { cleanJSON } = require("./src/utils");

var isNode = typeof window == "undefined";

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(fabricCanvas) {
  var Fabric = require("fabric").fabric;

  var Shapes = {
    Rectangle: require("./shapes/rectangle").klass,
    Circle: require("./shapes/circle").klass,
    Line: require("./shapes/line").klass,
    Arrow: require("./shapes/arrow").klass,
    Gap: require("./shapes/gap").klass,
  };

  var colorValues = {
    red: "#C1280B",
    orange: "#FF9024",
    yellow: "#F3E00E",
    green: "#16DC81",
    lightBlue: "#1BB1E9",
    blue: "#2343E8",
    darkBlue: "#2343E8",
    violet: "#DC54B7",
    pink: "#DC54B7",
    black: "#000000",
  };

  // Reference to the json object from processJSON
  var innerJSON;

  var imageOffset;
  var resizeRatio = 1;
  var finalWidth = 0;
  var markupObjects = [];

  var strokeWidth = null;
  var crop = null;

  /**
   * Array of delegate functions to draw a proper shape.
   */
  var addShapeDelegate = {
    arrow: function (data) {
      return drawLineBasedShape(Shapes.Arrow, data);
    },
    gap: function (data) {
      return drawLineBasedShape(Shapes.Gap, data);
    },
    line: function (data) {
      return drawLineBasedShape(Shapes.Line, data);
    },
    circle: drawCircle,
    rectangle: function (data) {
      // Because fabric considers top/left as the center of the rectangle.
      data.from.x -= data.size.width / 2;
      data.from.y -= data.size.height / 2;

      return drawRectangle(data);
    },
  };

  /**
   * Returns true if the given object is off the edge of the fabric canvas.
   * This is defined as whether the color stroke would be the only part of
   * the shape still visible at those coordinates.
   */
  fabricCanvas.isOffScreen = function (object) {
    var rect = object.getBoundingRect();
    return (
      rect.left + rect.width - strokeWidth * 2 < 0 ||
      rect.left + strokeWidth * 2 > this.width ||
      rect.top + rect.height - strokeWidth * 2 < 0 ||
      rect.top + strokeWidth * 2 > this.height
    );
  };

  function applyBackground(callback) {
    if (!isNode) {
      //Listen for shapes falling off the edge and delete
      fabricCanvas.on({
        "object:modified": function (e) {
          var shape = e.target;

          if (fabricCanvas.isOffScreen(shape)) remove(shape);
        }.bind(this),
      });
    }

    //Disable drag selection on canvas
    fabricCanvas.selection = false;

    if (!innerJSON.sourceFile) {
      if (!innerJSON.finalDimensions) {
        var msg = "Need source file or final dimensions to create canvas";
        throw new Error(msg);
      }

      //Apply markup to blank canvas
      applyMarkup(callback);
    } else {
      finalWidth = innerJSON.finalDimensions.width;
      if (isNode) {
        fabricCanvas.setBackgroundColor("#FFFFFF");
        require("fs").readFile(innerJSON.sourceFile, function (err, blob) {
          if (err) throw err;

          var dimensions = innerJSON.dimensions;
          var img = {
            width: dimensions.width,
            height: dimensions.height,
            originX: "left",
            originY: "top",
            src: blob,
          };

          Fabric.Image.fromObject(img, function (fimg) {
            var top = -imageOffset.y;
            if (top % 1 != 0) {
              top -= 0.5;
            }
            var left = -imageOffset.x;
            if (left % 1 != 0) {
              left -= 0.5;
            }

            fabricCanvas.add(fimg.set("top", top).set("left", left));
            applyMarkup(callback);
          });
        });
      } else {
        throw new Error("Source files not supported on frontend");
      }
    }
  }

  /**
   * Applies the given markup instructions to the fabric canvas. If this is
   * server-side, call writeCanvas() to write the results to a file. If
   * client-side, call the client-provided callback directly.
   */
  function applyMarkup(callback) {
    // strokeWidth needs to be caught now, since if it's lower in the JSON
    // it will not get picked up until it's too late.
    if (innerJSON.instructions.strokeWidth) {
      strokeWidth = innerJSON.instructions.strokeWidth;
    }

    for (var instruction in innerJSON.instructions) {
      switch (instruction) {
        case "draw":
          innerJSON.instructions.draw.forEach(function (e) {
            for (var shapeName in e) {
              var shape = e[shapeName];
              shape.shapeName = shapeName;

              switch (shapeName) {
                case "rectangle":
                  drawRectangle(shape);
                  break;
                case "circle":
                  drawCircle(shape);
                  break;
                case "line":
                  drawLineBasedShape(Shapes.Line, shape);
                  break;
                case "gap":
                  drawLineBasedShape(Shapes.Gap, shape);
                  break;
                case "arrow":
                  drawLineBasedShape(Shapes.Arrow, shape);
                  break;
                default:
                  console.error("Unsupported Shape: " + shapeName);
              }
            }
          });
          break;

        //These should already be taken care of
        case "crop":
        case "strokeWidth":
          break;
        default:
          console.error("Unsupported Instruction: " + instruction);
      }
    }

    if (isNode) {
      writeCanvas(callback);
    } else {
      callback(fabricCanvas);
    }
  }

  /**
   * Writes the processed canvas to a file.
   */
  function writeCanvas(callback) {
    fabricCanvas.renderAll();
    var outstream = require("fs").createWriteStream(innerJSON.destinationFile),
      stream = fabricCanvas.createJPEGStream({
        quality: 93,
        progressive: true,
      });

    stream.on("data", function (chunk) {
      outstream.write(chunk);
    });
    stream.on("end", function () {
      callback(fabricCanvas);
    });
  }

  /**
   * Returns a strokeWidth calculated based on the dimensions of the canvas.
   */
  function getStrokeWidth(finalWidth) {
    if (strokeWidth != null) {
      var width = strokeWidth;
    } else {
      var width = Math.max(Math.round((finalWidth / 300) * 2), 4);
    }

    return width;
  }

  function drawRectangle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var rect = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      width: shape.size.width,
      height: shape.size.height,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    //Fabric調整
    rect.top *= resizeRatio;
    rect.left *= resizeRatio;
    rect.width *= resizeRatio;
    rect.height *= resizeRatio;

    var fabricRect = new Shapes.Rectangle(rect);

    markupObjects.push(fabricRect);
    fabricCanvas.add(fabricRect);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricRect);
    }

    return fabricRect;
  }

  function drawLineBasedShape(klass, shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var line = {
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    var points = [
      (shape.from.x - imageOffset.x) * resizeRatio,
      (shape.from.y - imageOffset.y) * resizeRatio,
      (shape.to.x - imageOffset.x) * resizeRatio,
      (shape.to.y - imageOffset.y) * resizeRatio,
    ];

    var fabricLine = new klass(points, line);
    fabricLine.color = shape.color;

    markupObjects.push(fabricLine);
    fabricCanvas.add(fabricLine);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricLine);
    }

    return fabricLine;
  }

  function drawCircle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var circle = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      radius: shape.radius,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };
    circle.left *= resizeRatio;
    circle.top *= resizeRatio;
    circle.radius *= resizeRatio;

    var fabricCircle = new Shapes.Circle(circle);
    markupObjects.push(fabricCircle);
    fabricCanvas.add(fabricCircle);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricCircle);
    }

    return fabricCircle;
  }

  function locate(shape) {
    for (var i = 0; i < markupObjects.length; ++i) {
      if (markupObjects[i] === shape) {
        return i;
      }
    }

    return null;
  }

  function remove(shape) {
    var index = locate(shape);
    if (index != null) {
      fabricCanvas.remove(shape);
      markupObjects.splice(index, 1);
    }
  }

  /**
   * Performs ShapeData.type-agnostic attribute checks to ensure that the
   * necessary attributes are set in the given ShapeData object. Specifically,
   * the following operations are performed:
   *
   * - If data.x or data.y are not present, both are set to the center of
   *   the canvas. If both are present, they are scaled to the preview size
   *   of the canvas (if given).
   * - if data.color is not present, it is set to "red".
   */
  function normalizeShapeData(data) {
    data.from.x /= resizeRatio;
    data.from.y /= resizeRatio;
    data.from.x += imageOffset.x;
    data.from.y += imageOffset.y;

    if (data.to) {
      data.to.x /= resizeRatio;
      data.to.y /= resizeRatio;
      data.to.x += imageOffset.x;
      data.to.y += imageOffset.y;
    }

    if (!data.color) {
      data.color = "red";
    }
  }

  var publicInterface = {
    fabricCanvas: fabricCanvas,

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. The act of drawing is delegated to the proper
     * draw method based on its "type" attribute.
     */
    addShape: function addShape(data) {
      if (!data.type) throw "ShapeData.type is not defined.";

      normalizeShapeData(data);
      return addShapeDelegate[data.type](data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a circle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addCircle: function addCircle(data) {
      console.warn("Deprecated function: addCircle(). Use addShape() instead.");

      if (data.type !== "circle") data.type = "circle";
      return this.addShape(data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a rectangle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addRectangle: function addRectangle(data) {
      console.warn(
        "Deprecated function: addRectangle(). Use addShape() instead."
      );

      if (data.type !== "rectangle") data.type = "rectangle";
      return this.addShape(data);
    },

    /**
     * Sets the color of a tracked shape.
     */
    setColor: function setColor(shape, colorName) {
      shape.stroke = colorValues[colorName];
      shape.color = colorName;

      if (!isNode) fabricCanvas.renderAll();
    },

    /**
     * Returns an array of all objects in the fabric canvas, whether
     * tracked by the Builder or not.
     */
    getShapes: function getShapes() {
      return fabricCanvas._objects;
    },

    /**
     * Returns a reference to the currently selected shape.
     *
     * @return a reference to the currently selected shape.
     */
    getActiveShape: function getActiveShape() {
      return fabricCanvas.getActiveObject();
    },

    /**
     * Removes a specific tracked shape from the fabric canvas.
     */
    removeShape: function removeShape(shape) {
      remove(shape);
    },

    /**
     * Removes all tracked shapes from the fabric canvas.
     */
    removeShapes: function removeShapes() {
      for (var i = 0; i < markupObjects.length; ++i) {
        var shape = markupObjects[i];
        fabricCanvas.remove(shape);
      }

      markupObjects = [];
    },

    /**
     * Grows or shrinks the given shape depending on the given key
     * by the given number of pixels.
     *
     * @param shape The fabric shape to resize.
     * @param increment The number of pixels (negative or positive) to
     *  resize the shape.
     */
    incrementSize: function incrementSize(shape, increment) {
      if (!(typeof increment === "number")) {
        console.error("increment must be a number");
        return;
      }

      shape._withSizeLimitations(function () {
        shape.incrementSize(increment);
        if (fabricCanvas.isOffScreen(shape)) {
          shape.incrementSize(increment * -1);
        }
      }, true);

      shape.setCoords();
      fabricCanvas.renderAll();
    },

    /**
     * Takes a Builder-schema JSON object and performs the operations
     * listed therein, then calls the callback function.
     */
    processJSON: function processJSON(json, callback) {
      //Make sure not to render every addition on server end
      fabricCanvas.renderOnAddition = !isNode;
      fabricCanvas.uniScaleTransform = true;

      cleanJSON(json);

      innerJSON = json;

      crop = innerJSON.instructions.crop;
      imageOffset =
        typeof crop != "undefined"
          ? {
              x: crop.from.x,
              y: crop.from.y,
            }
          : {
              x: 0,
              y: 0,
            };

      if (innerJSON.resizeRatio) {
        resizeRatio = innerJSON.resizeRatio;
      }

      applyBackground(callback);
    },

    getMarkupObjects: function getMarkupObjects() {
      return markupObjects;
    },

    /**
     * Returns a markup string which can be used with ImageMarkupCall
     * to produce the same markup results as represented in this builder.
     */
    getMarkupString: function getMarkupString() {
      /**
       * Translate RGB value to applicable color string. Unknown
       * RGB values will will become black and a console.error message
       * will be written.
       */
      function translateRGBtoColorString(rgb) {
        for (var colorString in colorValues) {
          if (rgb == colorValues[colorString]) {
            return colorString;
          }
        }
        console.error("No mapping from RGB to Color String Found: " + rgb);
        return "black";
      }

      var markupString = ";";

      if (crop) {
        // Cut out cases where crop is used to offset non-4:3
        // images
        markupString +=
          "crop," +
          crop.from.x +
          "x" +
          crop.from.y +
          "," +
          crop.size.width +
          "x" +
          crop.size.height +
          ";";
      }

      for (var i = 0; i < markupObjects.length; ++i) {
        var object = markupObjects[i];

        switch (object.shapeName) {
          case "circle":
            var from = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var radius = Math.round(object.getRadiusX() / resizeRatio);
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "circle," +
              from.x +
              "x" +
              from.y +
              "," +
              radius +
              "," +
              color +
              ";";
            break;
          case "rectangle":
            var from = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var size = {
              width: Math.round(object.width / resizeRatio),
              height: Math.round(object.height / resizeRatio),
            };
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "rectangle," +
              from.x +
              "x" +
              from.y +
              "," +
              size.width +
              "x" +
              size.height +
              "," +
              color +
              ";";
            break;
          case "gap":
          case "arrow":
          case "line":
            markupString += object.toMarkup(resizeRatio, imageOffset);
            break;
          default:
            console.error("Unexpected object name: " + object.shapeName);
        }
      }

      return markupString;
    },
  };

  // Setup drag-to-draw for this canvas
  require("./src/drag_to_create").setup(publicInterface);

  return publicInterface;
}

module.exports.Builder = ImageMarkupBuilder;
<commit_msg>Fix `width` redeclaration in Builder<commit_after>const { cleanJSON } = require("./src/utils");

var isNode = typeof window == "undefined";

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(fabricCanvas) {
  var Fabric = require("fabric").fabric;

  var Shapes = {
    Rectangle: require("./shapes/rectangle").klass,
    Circle: require("./shapes/circle").klass,
    Line: require("./shapes/line").klass,
    Arrow: require("./shapes/arrow").klass,
    Gap: require("./shapes/gap").klass,
  };

  var colorValues = {
    red: "#C1280B",
    orange: "#FF9024",
    yellow: "#F3E00E",
    green: "#16DC81",
    lightBlue: "#1BB1E9",
    blue: "#2343E8",
    darkBlue: "#2343E8",
    violet: "#DC54B7",
    pink: "#DC54B7",
    black: "#000000",
  };

  // Reference to the json object from processJSON
  var innerJSON;

  var imageOffset;
  var resizeRatio = 1;
  var finalWidth = 0;
  var markupObjects = [];

  var strokeWidth = null;
  var crop = null;

  /**
   * Array of delegate functions to draw a proper shape.
   */
  var addShapeDelegate = {
    arrow: function (data) {
      return drawLineBasedShape(Shapes.Arrow, data);
    },
    gap: function (data) {
      return drawLineBasedShape(Shapes.Gap, data);
    },
    line: function (data) {
      return drawLineBasedShape(Shapes.Line, data);
    },
    circle: drawCircle,
    rectangle: function (data) {
      // Because fabric considers top/left as the center of the rectangle.
      data.from.x -= data.size.width / 2;
      data.from.y -= data.size.height / 2;

      return drawRectangle(data);
    },
  };

  /**
   * Returns true if the given object is off the edge of the fabric canvas.
   * This is defined as whether the color stroke would be the only part of
   * the shape still visible at those coordinates.
   */
  fabricCanvas.isOffScreen = function (object) {
    var rect = object.getBoundingRect();
    return (
      rect.left + rect.width - strokeWidth * 2 < 0 ||
      rect.left + strokeWidth * 2 > this.width ||
      rect.top + rect.height - strokeWidth * 2 < 0 ||
      rect.top + strokeWidth * 2 > this.height
    );
  };

  function applyBackground(callback) {
    if (!isNode) {
      //Listen for shapes falling off the edge and delete
      fabricCanvas.on({
        "object:modified": function (e) {
          var shape = e.target;

          if (fabricCanvas.isOffScreen(shape)) remove(shape);
        }.bind(this),
      });
    }

    //Disable drag selection on canvas
    fabricCanvas.selection = false;

    if (!innerJSON.sourceFile) {
      if (!innerJSON.finalDimensions) {
        var msg = "Need source file or final dimensions to create canvas";
        throw new Error(msg);
      }

      //Apply markup to blank canvas
      applyMarkup(callback);
    } else {
      finalWidth = innerJSON.finalDimensions.width;
      if (isNode) {
        fabricCanvas.setBackgroundColor("#FFFFFF");
        require("fs").readFile(innerJSON.sourceFile, function (err, blob) {
          if (err) throw err;

          var dimensions = innerJSON.dimensions;
          var img = {
            width: dimensions.width,
            height: dimensions.height,
            originX: "left",
            originY: "top",
            src: blob,
          };

          Fabric.Image.fromObject(img, function (fimg) {
            var top = -imageOffset.y;
            if (top % 1 != 0) {
              top -= 0.5;
            }
            var left = -imageOffset.x;
            if (left % 1 != 0) {
              left -= 0.5;
            }

            fabricCanvas.add(fimg.set("top", top).set("left", left));
            applyMarkup(callback);
          });
        });
      } else {
        throw new Error("Source files not supported on frontend");
      }
    }
  }

  /**
   * Applies the given markup instructions to the fabric canvas. If this is
   * server-side, call writeCanvas() to write the results to a file. If
   * client-side, call the client-provided callback directly.
   */
  function applyMarkup(callback) {
    // strokeWidth needs to be caught now, since if it's lower in the JSON
    // it will not get picked up until it's too late.
    if (innerJSON.instructions.strokeWidth) {
      strokeWidth = innerJSON.instructions.strokeWidth;
    }

    for (var instruction in innerJSON.instructions) {
      switch (instruction) {
        case "draw":
          innerJSON.instructions.draw.forEach(function (e) {
            for (var shapeName in e) {
              var shape = e[shapeName];
              shape.shapeName = shapeName;

              switch (shapeName) {
                case "rectangle":
                  drawRectangle(shape);
                  break;
                case "circle":
                  drawCircle(shape);
                  break;
                case "line":
                  drawLineBasedShape(Shapes.Line, shape);
                  break;
                case "gap":
                  drawLineBasedShape(Shapes.Gap, shape);
                  break;
                case "arrow":
                  drawLineBasedShape(Shapes.Arrow, shape);
                  break;
                default:
                  console.error("Unsupported Shape: " + shapeName);
              }
            }
          });
          break;

        //These should already be taken care of
        case "crop":
        case "strokeWidth":
          break;
        default:
          console.error("Unsupported Instruction: " + instruction);
      }
    }

    if (isNode) {
      writeCanvas(callback);
    } else {
      callback(fabricCanvas);
    }
  }

  /**
   * Writes the processed canvas to a file.
   */
  function writeCanvas(callback) {
    fabricCanvas.renderAll();
    var outstream = require("fs").createWriteStream(innerJSON.destinationFile),
      stream = fabricCanvas.createJPEGStream({
        quality: 93,
        progressive: true,
      });

    stream.on("data", function (chunk) {
      outstream.write(chunk);
    });
    stream.on("end", function () {
      callback(fabricCanvas);
    });
  }

  /**
   * Returns a strokeWidth calculated based on the dimensions of the canvas.
   */
  function getStrokeWidth(finalWidth) {
    return strokeWidth != null
      ? strokeWidth
      : Math.max(Math.round((finalWidth / 300) * 2), 4);
  }

  function drawRectangle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var rect = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      width: shape.size.width,
      height: shape.size.height,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    //Fabric調整
    rect.top *= resizeRatio;
    rect.left *= resizeRatio;
    rect.width *= resizeRatio;
    rect.height *= resizeRatio;

    var fabricRect = new Shapes.Rectangle(rect);

    markupObjects.push(fabricRect);
    fabricCanvas.add(fabricRect);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricRect);
    }

    return fabricRect;
  }

  function drawLineBasedShape(klass, shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var line = {
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };

    var points = [
      (shape.from.x - imageOffset.x) * resizeRatio,
      (shape.from.y - imageOffset.y) * resizeRatio,
      (shape.to.x - imageOffset.x) * resizeRatio,
      (shape.to.y - imageOffset.y) * resizeRatio,
    ];

    var fabricLine = new klass(points, line);
    fabricLine.color = shape.color;

    markupObjects.push(fabricLine);
    fabricCanvas.add(fabricLine);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricLine);
    }

    return fabricLine;
  }

  function drawCircle(shape) {
    shape.stroke = getStrokeWidth(finalWidth);

    var circle = {
      left: shape.from.x - imageOffset.x,
      top: shape.from.y - imageOffset.y,
      radius: shape.radius,
      borderWidth: shape.stroke,
      stroke: colorValues[shape.color],
      color: shape.color,
    };
    circle.left *= resizeRatio;
    circle.top *= resizeRatio;
    circle.radius *= resizeRatio;

    var fabricCircle = new Shapes.Circle(circle);
    markupObjects.push(fabricCircle);
    fabricCanvas.add(fabricCircle);

    if (!isNode) {
      // Set this as the active object
      fabricCanvas.setActiveObject(fabricCircle);
    }

    return fabricCircle;
  }

  function locate(shape) {
    for (var i = 0; i < markupObjects.length; ++i) {
      if (markupObjects[i] === shape) {
        return i;
      }
    }

    return null;
  }

  function remove(shape) {
    var index = locate(shape);
    if (index != null) {
      fabricCanvas.remove(shape);
      markupObjects.splice(index, 1);
    }
  }

  /**
   * Performs ShapeData.type-agnostic attribute checks to ensure that the
   * necessary attributes are set in the given ShapeData object. Specifically,
   * the following operations are performed:
   *
   * - If data.x or data.y are not present, both are set to the center of
   *   the canvas. If both are present, they are scaled to the preview size
   *   of the canvas (if given).
   * - if data.color is not present, it is set to "red".
   */
  function normalizeShapeData(data) {
    data.from.x /= resizeRatio;
    data.from.y /= resizeRatio;
    data.from.x += imageOffset.x;
    data.from.y += imageOffset.y;

    if (data.to) {
      data.to.x /= resizeRatio;
      data.to.y /= resizeRatio;
      data.to.x += imageOffset.x;
      data.to.y += imageOffset.y;
    }

    if (!data.color) {
      data.color = "red";
    }
  }

  var publicInterface = {
    fabricCanvas: fabricCanvas,

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. The act of drawing is delegated to the proper
     * draw method based on its "type" attribute.
     */
    addShape: function addShape(data) {
      if (!data.type) throw "ShapeData.type is not defined.";

      normalizeShapeData(data);
      return addShapeDelegate[data.type](data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a circle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addCircle: function addCircle(data) {
      console.warn("Deprecated function: addCircle(). Use addShape() instead.");

      if (data.type !== "circle") data.type = "circle";
      return this.addShape(data);
    },

    /**
     * Adds and tracks a given data object following the ShapeData schema
     * to the fabric canvas. This ignores the "type" attribute of the object
     * and just adds a rectangle. To be deprecated by addShape().
     *
     * @return a reference to the tracked shape.
     */
    addRectangle: function addRectangle(data) {
      console.warn(
        "Deprecated function: addRectangle(). Use addShape() instead."
      );

      if (data.type !== "rectangle") data.type = "rectangle";
      return this.addShape(data);
    },

    /**
     * Sets the color of a tracked shape.
     */
    setColor: function setColor(shape, colorName) {
      shape.stroke = colorValues[colorName];
      shape.color = colorName;

      if (!isNode) fabricCanvas.renderAll();
    },

    /**
     * Returns an array of all objects in the fabric canvas, whether
     * tracked by the Builder or not.
     */
    getShapes: function getShapes() {
      return fabricCanvas._objects;
    },

    /**
     * Returns a reference to the currently selected shape.
     *
     * @return a reference to the currently selected shape.
     */
    getActiveShape: function getActiveShape() {
      return fabricCanvas.getActiveObject();
    },

    /**
     * Removes a specific tracked shape from the fabric canvas.
     */
    removeShape: function removeShape(shape) {
      remove(shape);
    },

    /**
     * Removes all tracked shapes from the fabric canvas.
     */
    removeShapes: function removeShapes() {
      for (var i = 0; i < markupObjects.length; ++i) {
        var shape = markupObjects[i];
        fabricCanvas.remove(shape);
      }

      markupObjects = [];
    },

    /**
     * Grows or shrinks the given shape depending on the given key
     * by the given number of pixels.
     *
     * @param shape The fabric shape to resize.
     * @param increment The number of pixels (negative or positive) to
     *  resize the shape.
     */
    incrementSize: function incrementSize(shape, increment) {
      if (!(typeof increment === "number")) {
        console.error("increment must be a number");
        return;
      }

      shape._withSizeLimitations(function () {
        shape.incrementSize(increment);
        if (fabricCanvas.isOffScreen(shape)) {
          shape.incrementSize(increment * -1);
        }
      }, true);

      shape.setCoords();
      fabricCanvas.renderAll();
    },

    /**
     * Takes a Builder-schema JSON object and performs the operations
     * listed therein, then calls the callback function.
     */
    processJSON: function processJSON(json, callback) {
      //Make sure not to render every addition on server end
      fabricCanvas.renderOnAddition = !isNode;
      fabricCanvas.uniScaleTransform = true;

      cleanJSON(json);

      innerJSON = json;

      crop = innerJSON.instructions.crop;
      imageOffset =
        typeof crop != "undefined"
          ? {
              x: crop.from.x,
              y: crop.from.y,
            }
          : {
              x: 0,
              y: 0,
            };

      if (innerJSON.resizeRatio) {
        resizeRatio = innerJSON.resizeRatio;
      }

      applyBackground(callback);
    },

    getMarkupObjects: function getMarkupObjects() {
      return markupObjects;
    },

    /**
     * Returns a markup string which can be used with ImageMarkupCall
     * to produce the same markup results as represented in this builder.
     */
    getMarkupString: function getMarkupString() {
      /**
       * Translate RGB value to applicable color string. Unknown
       * RGB values will will become black and a console.error message
       * will be written.
       */
      function translateRGBtoColorString(rgb) {
        for (var colorString in colorValues) {
          if (rgb == colorValues[colorString]) {
            return colorString;
          }
        }
        console.error("No mapping from RGB to Color String Found: " + rgb);
        return "black";
      }

      var markupString = ";";

      if (crop) {
        // Cut out cases where crop is used to offset non-4:3
        // images
        markupString +=
          "crop," +
          crop.from.x +
          "x" +
          crop.from.y +
          "," +
          crop.size.width +
          "x" +
          crop.size.height +
          ";";
      }

      for (var i = 0; i < markupObjects.length; ++i) {
        var object = markupObjects[i];

        switch (object.shapeName) {
          case "circle":
            var from = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var radius = Math.round(object.getRadiusX() / resizeRatio);
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "circle," +
              from.x +
              "x" +
              from.y +
              "," +
              radius +
              "," +
              color +
              ";";
            break;
          case "rectangle":
            var from = {
              x: Math.round(object.left / resizeRatio) + imageOffset.x,
              y: Math.round(object.top / resizeRatio) + imageOffset.y,
            };
            var size = {
              width: Math.round(object.width / resizeRatio),
              height: Math.round(object.height / resizeRatio),
            };
            var color = translateRGBtoColorString(object.stroke);

            markupString +=
              "rectangle," +
              from.x +
              "x" +
              from.y +
              "," +
              size.width +
              "x" +
              size.height +
              "," +
              color +
              ";";
            break;
          case "gap":
          case "arrow":
          case "line":
            markupString += object.toMarkup(resizeRatio, imageOffset);
            break;
          default:
            console.error("Unexpected object name: " + object.shapeName);
        }
      }

      return markupString;
    },
  };

  // Setup drag-to-draw for this canvas
  require("./src/drag_to_create").setup(publicInterface);

  return publicInterface;
}

module.exports.Builder = ImageMarkupBuilder;
<|endoftext|>


<commit_before>define([
    'comindware/core', 'demoPage/views/CanvasView', 'demoPage/views/DemoProfilePanelView'
], function (core, CanvasView, DemoProfilePanelView) {
    'use strict';
    return function () {
        var DemoButtonView = Marionette.ItemView.extend({
            template: function () {
                return Handlebars.compile('Demo Button');
            },
            tagName: 'span'
        });

        var popout = core.dropdown.factory.createPopout({
            buttonView: DemoButtonView,
            panelView: DemoProfilePanelView,
            popoutFlow: 'right'
        });

        return new CanvasView({
            view: popout,
            canvas: {
                height: '800px',
                display: 'flex',
                'flex-flow': 'column',
                'justify-content': 'flex-end'
            },
            region: {
                height: 'auto'
            }
        });
    };
});
<commit_msg>popout: demo-page clean up<commit_after>define([
    'comindware/core', 'demoPage/views/CanvasView', 'demoPage/views/DemoProfilePanelView'
], function (core, CanvasView, DemoProfilePanelView) {
    'use strict';
    return function () {
        var DemoButtonView = Marionette.ItemView.extend({
            template: function () {
                return Handlebars.compile('Demo Button');
            },
            tagName: 'span'
        });

        var popout = core.dropdown.factory.createPopout({
            buttonView: DemoButtonView,
            panelView: DemoProfilePanelView,
            popoutFlow: 'right'
        });

        return new CanvasView({
            view: popout,
            canvas: {
                height: '30px'
            }
        });
    };
});
<|endoftext|>


<commit_before>var roleBuilder = require('role.builder');
var roleCollector = require('role.collector');

module.exports = {
    // a function to run the logic for this role
    run: function(creep) {
        // if creep is trying to repair something but has no energy left
        if (creep.carry.energy == 0) {
            // switch state
            creep.memory.working = false;
        }
        // if creep is full of energy but not working
        else if (creep.carry.energy == creep.carryCapacity) {
            // switch state
            creep.memory.working = true;
        }

        // if creep is supposed to repair something
        if (creep.memory.working == true) {

			var walls = creep.room.find(FIND_STRUCTURES, { filter: (s) => s.structureType == STRUCTURE_WALL || s.structureType == STRUCTURE_RAMPART});
			var target = undefined;

			// loop with increasing percentages
			for (var percentage = 0.0001; percentage <= 1; percentage = percentage + 0.0001){
				// find a wall with less than percentage hits

				// for some reason this doesn't work
				// target = creep.pos.findClosestByPath(walls, {
				//     filter: (s) => s.hits / s.hitsMax < percentage
				// });

				// so we have to use this
				target = creep.pos.findClosestByPath(FIND_STRUCTURES, {filter: (s) => s.structureType == STRUCTURE_WALL && s.hits / s.hitsMax < percentage});

				if (target != undefined) {
					break;
				}
			}

            //console.log(target);
            // if we find a wall that has to be repaired
            if (target != undefined) {
                // try to repair it, if not in range
                if (creep.repair(target) == ERR_NOT_IN_RANGE) {
                    // move towards it
                    creep.moveTo(target, {reusePath: 5});
                }

            }
            // if we can't fine one
            else {
                // look for construction sites
                roleBuilder.run(creep);
            }
        }
        // if creep is supposed to harvest energy from source
        else {
            roleCollector.run(creep);
        }
    }
};<commit_msg>Flag narrowSource changed to new system / added README<commit_after>var roleBuilder = require('role.builder');
var roleCollector = require('role.collector');

module.exports = {
    // a function to run the logic for this role
    run: function(creep) {
        // if creep is trying to repair something but has no energy left
        if (creep.carry.energy == 0) {
            // switch state
            creep.memory.working = false;
        }
        // if creep is full of energy but not working
        else if (creep.carry.energy == creep.carryCapacity) {
            // switch state
            creep.memory.working = true;
        }

        // if creep is supposed to repair something
        if (creep.memory.working == true) {

			var walls = creep.room.find(FIND_STRUCTURES, { filter: (s) => s.structureType == STRUCTURE_WALL || s.structureType == STRUCTURE_RAMPART});
			var target = undefined;

			// loop with increasing percentages
			for (var percentage = 0.0001; percentage <= 1; percentage = percentage + 0.0001){
				target = creep.pos.findClosestByPath(FIND_STRUCTURES, {filter: (s) => (s.structureType == STRUCTURE_WALL || s.structureType == STRUCTURE_RAMPART) && s.hits / s.hitsMax < percentage});

				if (target != undefined) {
					break;
				}
			}

            // if we find a wall that has to be repaired
            if (target != undefined) {
                // try to repair it, if not in range
                if (creep.repair(target) == ERR_NOT_IN_RANGE) {
                    // move towards it
                    creep.moveTo(target, {reusePath: 5});
                }

            }
            // if we can't fine one
            else {
                // look for construction sites
                roleBuilder.run(creep);
            }
        }
        // if creep is supposed to harvest energy from source
        else {
            roleCollector.run(creep);
        }
    }
};<|endoftext|>