text: "use strict";
  
  var _ = require('./lodash');
  var $ = require('jquery');
  
  var Scribe = require('scribe-editor');
  var scribePluginFormatterPlainTextConvertNewLinesToHTML = require('scribe-plugin-formatter-plain-text-convert-new-lines-to-html');
  var scribePluginLinkPromptCommand = require('scribe-plugin-link-prompt-command');
  
  var config = require('./config');
  var utils = require('./utils');
  var stToMarkdown = require('./to-markdown');
  var BlockMixins = require('./block_mixins');
  
  var SimpleBlock = require('./simple-block');
  var BlockReorder = require('./block-reorder');
  var BlockDeletion = require('./block-deletion');
  var BlockPositioner = require('./block-positioner');
  var EventBus = require('./event-bus');
  
  var Spinner = require('spin.js');
  
  var Block = function(data, instance_id, mediator, options) {
    SimpleBlock.apply(this, arguments);
  };
  
  Block.prototype = Object.create(SimpleBlock.prototype);
  Block.prototype.constructor = Block;
  
  var delete_template = [
    "<div class='st-block__ui-delete-controls'>",
    "<label class='st-block__delete-label'>",
    "<%= i18n.t('general:delete') %>",
    "</label>",
    "<a class='st-block-ui-btn st-block-ui-btn--confirm-delete st-icon' data-icon='tick'></a>",
    "<a class='st-block-ui-btn st-block-ui-btn--deny-delete st-icon' data-icon='close'></a>",
    "</div>"
  ].join("\n");
  
  Object.assign(Block.prototype, SimpleBlock.fn, require('./block-validations'), {
  
    bound: [
      "_handleContentPaste", "_onFocus", "_onBlur", "onDrop", "onDeleteClick",
      "clearInsertedStyles", "getSelectionForFormatter", "onBlockRender",
    ],
  
    className: 'st-block st-icon--add',
  
    attributes: function() {
      return Object.assign(SimpleBlock.fn.attributes.call(this), {
        'data-icon-after' : "add"
      });
    },
  
    icon_name: 'default',
  
    validationFailMsg: function() {
      return i18n.t('errors:validation_fail', { type: this.title() });
    },
  
    editorHTML: '<div class="st-block__editor"></div>',
  
    toolbarEnabled: true,
  
    availableMixins: ['droppable', 'pastable', 'uploadable', 'fetchable',
      'ajaxable', 'controllable'],
  
    droppable: false,
    pastable: false,
    uploadable: false,
    fetchable: false,
    ajaxable: false,
  
    drop_options: {},
    paste_options: {},
    upload_options: {},
  
    formattable: true,
  
    _previousSelection: '',
  
    initialize: function() {},
  
    toMarkdown: function(markdown){ return markdown; },
    toHTML: function(html){ return html; },
  
    withMixin: function(mixin) {
      if (!_.isObject(mixin)) { return; }
  
      var initializeMethod = "initialize" + mixin.mixinName;
  
      if (_.isUndefined(this[initializeMethod])) {
        Object.assign(this, mixin);
        this[initializeMethod]();
      }
    },
  
    render: function() {
      this.beforeBlockRender();
      this._setBlockInner();
  
      this.$editor = this.$inner.children().first();
  
      if(this.droppable || this.pastable || this.uploadable) {
        var input_html = $("<div>", { 'class': 'st-block__inputs' });
        this.$inner.append(input_html);
        this.$inputs = input_html;
      }
  
      if (this.hasTextBlock) { this._initTextBlocks(); }
  
      this.availableMixins.forEach(function(mixin) {
        if (this[mixin]) {
          this.withMixin(BlockMixins[utils.classify(mixin)]);
        }
      }, this);
  
      if (this.formattable) { this._initFormatting(); }
  
      this._blockPrepare();
  
      return this;
    },
  
    remove: function() {
      if (this.ajaxable) {
        this.resolveAllInQueue();
      }
  
      this.$el.remove();
    },
  
    loading: function() {
      if(!_.isUndefined(this.spinner)) { this.ready(); }
  
      this.spinner = new Spinner(config.defaults.spinner);
      this.spinner.spin(this.$el[0]);
  
      this.$el.addClass('st--is-loading');
    },
  
    ready: function() {
      this.$el.removeClass('st--is-loading');
      if (!_.isUndefined(this.spinner)) {
        this.spinner.stop();
        delete this.spinner;
      }
    },
  
    /* Generic _serializeData implementation to serialize the block into a plain object.
     * Can be overwritten, although hopefully this will cover most situations.
     * If you want to get the data of your block use block.getBlockData()
     */
    _serializeData: function() {
      utils.log("toData for " + this.blockID);
  
      var data = {};
  
      /* Simple to start. Add conditions later */
      if (this.hasTextBlock()) {
        data.text = this.getTextBlockHTML();
        if (data.text.length > 0 && this.options.convertToMarkdown) {
          data.text = stToMarkdown(data.text, this.type);
        }
      }
  
      // Add any inputs to the data attr
      if (this.$(':input').not('.st-paste-block').length > 0) {
        this.$(':input').each(function(index,input){
          if (input.getAttribute('name')) {
            data[input.getAttribute('name')] = input.value;
          }
        });
      }
  
      return data;
    },
  
    /* Generic implementation to tell us when the block is active */
    focus: function() {
      this.getTextBlock().focus();
    },
  
    blur: function() {
      this.getTextBlock().blur();
    },
  
    onFocus: function() {
      this.getTextBlock().bind('focus', this._onFocus);
    },
  
    onBlur: function() {
      this.getTextBlock().bind('blur', this._onBlur);
    },
  
    /*
     * Event handlers
     */
  
    _onFocus: function() {
      this.trigger('blockFocus', this.$el);
    },
  
    _onBlur: function() {},
  
    onBlockRender: function() {
      this.focus();
    },
  
    onDrop: function(dataTransferObj) {},
  
    onDeleteClick: function(ev) {
      ev.preventDefault();
  
      var onDeleteConfirm = function(e) {
        e.preventDefault();
        this.mediator.trigger('block:remove', this.blockID);
        this.remove();
      };
  
      var onDeleteDeny = function(e) {
        e.preventDefault();
        this.$el.removeClass('st-block--delete-active');
        $delete_el.remove();
      };
  
      if (this.isEmpty()) {
        onDeleteConfirm.call(this, new Event('click'));
        return;
      }
  
      this.$inner.append(_.template(delete_template));
      this.$el.addClass('st-block--delete-active');
  
      var $delete_el = this.$inner.find('.st-block__ui-delete-controls');
  
      this.$inner.on('click', '.st-block-ui-btn--confirm-delete',
                     onDeleteConfirm.bind(this))
                     .on('click', '.st-block-ui-btn--deny-delete',
                         onDeleteDeny.bind(this));
    },
  
    beforeLoadingData: function() {
      this.loading();
  
      if(this.droppable || this.uploadable || this.pastable) {
        this.$editor.show();
        this.$inputs.hide();
      }
  
      SimpleBlock.fn.beforeLoadingData.call(this);
  
      this.ready();
    },
  
    execTextBlockCommand: function(cmdName) {
      if (_.isUndefined(this._scribe)) {
        throw "No Scribe instance found to send a command to";
      }
      var cmd = this._scribe.getCommand(cmdName);
      this._scribe.el.focus();
      cmd.execute();
    },
  
    queryTextBlockCommandState: function(cmdName) {
      if (_.isUndefined(this._scribe)) {
        throw "No Scribe instance found to query command";
      }
      var cmd = this._scribe.getCommand(cmdName),
          sel = new this._scribe.api.Selection();
      return sel.range && cmd.queryState();
    },
  
    _handleContentPaste: function(ev) {
      setTimeout(this.onContentPasted.bind(this, ev, $(ev.currentTarget)), 0);
    },
  
    _getBlockClass: function() {
      return 'st-block--' + this.className;
    },
  
    /*
     * Init functions for adding functionality
     */
  
    _initUIComponents: function() {
  
      var positioner = new BlockPositioner(this.$el, this.mediator);
  
      this._withUIComponent(positioner, '.st-block-ui-btn--reorder',
                            positioner.toggle);
  
      this._withUIComponent(new BlockReorder(this.$el, this.mediator));
  
      this._withUIComponent(new BlockDeletion(), '.st-block-ui-btn--delete',
                            this.onDeleteClick);
  
      this.onFocus();
      this.onBlur();
    },
  
    _initFormatting: function() {
      // Enable formatting keyboard input
      var block = this;
  
      if (!this.options.formatBar) {
        return;
      }
  
      this.options.formatBar.commands.forEach(function(cmd) {
        if (_.isUndefined(cmd.keyCode)) {
          return;
        }
  
        var ctrlDown = false;
  
        block.$el
          .on('keyup','.st-text-block', function(ev) {
            if(ev.which === 17 || ev.which === 224 || ev.which === 91) {
              ctrlDown = false;
            }
          })
          .on('keydown','.st-text-block', {formatter: cmd}, function(ev) {
            if(ev.which === 17 || ev.which === 224 || ev.which === 91) {
              ctrlDown = true;
            }
  
            if(ev.which === ev.data.formatter.keyCode && ctrlDown) {
              ev.preventDefault();
              block.execTextBlockCommand(ev.data.formatter.cmd);
            }
          });
      });
    },
  
    _initTextBlocks: function() {
      this.getTextBlock()
          .bind('keyup', this.getSelectionForFormatter)
          .bind('mouseup', this.getSelectionForFormatter)
          .bind('DOMNodeInserted', this.clearInsertedStyles);
  
      var textBlock = this.getTextBlock().get(0);
      if (!_.isUndefined(textBlock) && _.isUndefined(this._scribe)) {
        this._scribe = new Scribe(textBlock, {
          debug: config.scribeDebug,
        });
        this._scribe.use(scribePluginFormatterPlainTextConvertNewLinesToHTML());
        this._scribe.use(scribePluginLinkPromptCommand());
  
        if (_.isFunction(this.options.configureScribe)) {
          this.options.configureScribe.call(this, this._scribe);
        }
      }
    },
  
    getSelectionForFormatter: function() {
      var block = this;
      setTimeout(function() {
        var selection = window.getSelection(),
            selectionStr = selection.toString().trim(),
            en = 'formatter:' + ((selectionStr === '') ? 'hide' : 'position');
  
        block.mediator.trigger(en, block);
        EventBus.trigger(en, block);
      }, 1);
    },
  
    clearInsertedStyles: function(e) {
      var target = e.target;
      target.removeAttribute('style'); // Hacky fix for Chrome.
    },
  
    hasTextBlock: function() {
      return this.getTextBlock().length > 0;
    },
  
    getTextBlock: function() {
      if (_.isUndefined(this.text_block)) {
        this.text_block = this.$('.st-text-block');
      }
  
      return this.text_block;
    },
  
    getTextBlockHTML: function() {
      return this._scribe.getHTML();
    },
  
    setTextBlockHTML: function(html) {
      return this._scribe.setContent(html);
    },
  
    isEmpty: function() {
      return _.isEmpty(this.getBlockData());
    }
  
  });
  
  Block.extend = require('./helpers/extend'); // Allow our Block to be extended.
  
  module.exports = Block;
  