<commit_before>define(function() {

  'use strict';

  function VolumeDescriptorView(buffer, byteOffset, byteLength) {
    this.dv = new DataView(buffer, byteOffset, byteLength);
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
  }
  VolumeDescriptorView.prototype = {
    get typeCode() {
      return this.bytes[0];
    },
    get descriptorType() {
      switch (this.typeCode) {
        case 0: return 'boot';
        case 1: return 'volume'; // primary
        case 2: return 'volume'; // supplementary
        case 3: return 'partition';
        case 255: return 'terminator';
        default: return this.typeCode;
      }
    },
    get isPrimaryVolume() {
      return this.typeCode === 1;
    },
    get signature() {
      return String.fromCharCode(this.bytes[1], this.bytes[2], this.bytes[3], this.bytes[4], this.bytes[5]);
    },
    get hasValidSignature() {
      return this.signature === 'CD001';
    },
    get version() {
      return this.bytes[6];
    },
    get body() {
      var body, buffer = this.dv.buffer, byteOffset = this.dv.byteOffset, byteLength = this.dv.byteLength;
      switch(this.descriptorType) {
        case 'boot': body = new BootRecordView(buffer, byteOffset, byteLength); break;
        case 'volume': body = new VolumeRecordView(buffer, byteOffset, byteLength); break;
        default: body = null; break;
      }
      Object.defineProperty(this, 'body', {value:body});
      return body;
    },
  };

  function readSpacePadded(bytes) {
    return String.fromCharCode.apply(null, bytes).replace(/\s*$/, '');
  }

  function decodeTimeZoneSuffix(tz) {
    tz = (tz - 48) * 15;
    var sign = (tz < 0) ? ('-') : ('+');
    tz = Math.abs(tz);
    return sign + ('0' + Math.floor(tz / 60)).slice(-2) + ':' + ('0' + tz%60).slice(-2);
  }

  function readStringDateTime(bytes, offset) {
    var year = String.fromCharCode(bytes[offset + 0], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);
    if (!/^0*[1-9][0-9]*$/.test(year)) return null;
    var month = String.fromCharCode(bytes[offset + 4], bytes[offset + 5]);
    var day = String.fromCharCode(bytes[offset + 6], bytes[offset + 7]);
    var hour = String.fromCharCode(bytes[offset + 8], bytes[offset + 9]);
    var minute = String.fromCharCode(bytes[offset + 10], bytes[offset + 11]);
    var second = String.fromCharCode(bytes[offset + 12], bytes[offset + 13]);
    var hundredths = String.fromCharCode(bytes[offset + 14], bytes[offset + 15]);
    var timeZone = decodeTimeZoneSuffix(bytes[offset + 16]);
    var dateString = year+'-'+month+'-'+day+'T'+hour+':'+minute+':'+second+'.'+hundredths+timeZone;
    console.log(dateString);
    return new Date(dateString);
  }

  function readBinaryDateTime(bytes, offset) {
    if (bytes[offset + 1] === 0) return null; // month, must be 1-12
    var year = '' + (1900 + bytes[offset]);
    var month = ('0' + bytes[offset + 1]).slice(-2);
    var day = ('0' + bytes[offset + 2]).slice(-2);
    var hour = ('0' + bytes[offset + 3]).slice(-2);
    var minute = ('0' + bytes[offset + 4]).slice(-2);
    var second = ('0' + bytes[offset + 5]).slice(-2);
    var timeZone = decodeTimeZoneSuffix(bytes[offset + 6]);
    var dateString = year + '-' + month + '-' + day + 'T' + hour + ':' + minute + ':' + second + timeZone;
    console.log(dateString);
    return new Date(dateString);
  }

  function BootRecordView(buffer, byteOffset, byteLength) {
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
    this.bootData = this.bytes.subarray(71);
  }
  BootRecordView.prototype = {
    get systemIdentifier() {
      return readSpacePadded(this.bytes.subarray(7, 32));
    },
    get identifier() {
      return readSpacePadded(this.bytes.subarray(39, 64));
    },
  };

  function VolumeRecordView(buffer, byteOffset, byteLength) {
    this.dv = new DataView(buffer, byteOffset, byteLength);
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
  }
  VolumeRecordView.prototype = {
    get systemIdentifier() {
      return readSpacePadded(this.bytes.subarray(8, 32));
    },
    get identifier() {
      return readSpacePadded(this.bytes.subarray(40, 64));
    },
    get blockCount() {
      return this.dv.getUint32(80, true);
    },
    get diskCount() {
      return this.dv.getUint16(120, true);
    },
    get diskNumber() {
      return this.dv.getUint16(124, true);
    },
    get blockByteLength() {
      return this.dv.getUint16(128, true);
    },
    get pathTableByteLength() {
      return this.dv.getUint32(132, true);
    },
    get pathTableBlockAddressLE() {
      return this.dv.getUint32(140, true);
    },
    get optionalPathTableBlockAddressLE() {
      return this.dv.getUint32(144, true);
    },
    get pathTableBlockAddressLE() {
      return this.dv.getUint32(148, false);
    },
    get optionalPathTableBlockAddressLE() {
      return this.dv.getUint32(152, false);
    },
    get offsetof_rootDirectory() {
      return 156;
    },
    get rootDirectory() {
      var root = new DirectoryRecordView(this.dv.buffer, this.dv.byteOffset + 156, 34);
      Object.defineProperty(this, 'rootDirectory', {value:root});
      return root;
    },
    get setIdentifier() {
      return readStringPadded(this.bytes.subarray(190, 190 + 128));
    },
    get publisher() {
      return readStringPadded(this.bytes.subarray(318, 318 + 128));
    },
    get dataPreparer() {
      return readStringPadded(this.bytes.subarray(446, 446 + 128));
    },
    get application() {
      return readStringPadded(this.bytes.subarray(574, 574 + 128));
    },
    get copyrightFilename() {
      return readStringPadded(this.bytes.subarray(702, 702 + 38));
    },
    get abstractFilename() {
      return readStringPadded(this.bytes.subarray(740, 740 + 36));
    },
    get bibliographyFilename() {
      return readStringPadded(this.bytes.subarray(776, 776 + 37));
    },
    get createdAt() {
      return readStringDateTime(this.bytes, 813);
    },
    get modifiedAt() {
      return readStringDateTime(this.bytes, 830);
    },
    get becomesObsoleteAt() {
      return readStringDateTime(this.bytes, 847);
    },
    get becomesEffectiveAt() {
      return readStringDateTime(this.bytes, 864);
    },
    get fileStructureVersion() {
      return this.bytes[881];
    },
    get applicationData() {
      var section = this.bytes.subarray(883, 883 + 512);
      Object.defineProperty(this, 'applicationData', {value:section});
      return section;
    },
  };

  function DirectoryRecordView(buffer, byteOffset, byteLength) {
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
    this.dv = new DataView(buffer, byteOffset, byteLength);
  }
  DirectoryRecordView.prototype = {
    get length() {
      return this.bytes[0];
    },
    get extendedAttributeRecordLength() {
      return this.bytes[1];
    },
    get dataBlockAddress() {
      return this.dv.getUint32(2, true);
    },
    get dataByteLength() {
      return this.dv.getUint32(10, true);
    },
    get recordedAt() {
      return readBinaryDateTime(this.bytes, 18);
    },
    get flags() {
      return this.bytes[25];
    },
    get isHidden() {
      return !!(this.flags & (1 << 0));
    },
    get isDirectory() {
      return !!(this.flags & (1 << 1));
    },
    get isAssociatedFile() {
      return !!(this.flags & (1 << 2));
    },
    get hasExtendedAttributeInfo() {
      return !!(this.flags & (1 << 3));
    },
    get hasPermissionsInExtendedAttributeInfo() {
      return !!(this.flags & (1 << 4));
    },
    get isPartial() {
      return !!(this.flags & (1 << 7));
    },
    get interleavedUnitSize() {
      return this.bytes[26];
    },
    get interleavedGapSize() {
      return this.bytes[27];
    },
    get volumeNumber() {
      return this.dv.getUint16(28, true);
    },
    get nameAndIdentifier() {
      return String.fromCharCode.apply(null, this.bytes.subarray(33, 33 + this.bytes[32]));
    },
    get name() {
      return this.nameAndIdentifier.replace(/;\d+$/, '');
    },
    get identifier() {
      return +(this.nameAndIdentifier.match(/;(\d+)$/) || '')[1];
    },
    get after_identifier() {
      var identifierLength = this.bytes[32];
      return 33 + identifierLength + ((identifierLength + 1) % 2);
    },
    get byteLength() {
      var extendedLength = this.extendedAttributeRecordLength;
      return this.after_identifier + extendedLength + extendedLength % 2;
    },
  };

  function PathTableEntryView(littleEndian, buffer, byteOffset, byteLength) {
    this.littleEndian = littleEndian;
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
    this.dv = new DataView(buffer, byteOffset, byteLength);
  }
  PathTableEntryView.prototype = {
    get extendedAttributeLength() {
      return this.bytes[1];
    },
    get extentBlockAddress() {
      return this.dv.getUint32(2, this.littleEndian);
    },
    get parentDirectoryIndex() {
      return this.dv.getUint16(6, this.littleEndian);
    },
    get identifier() {
      return String.fromCharCode.apply(null, this.bytes.subarray(8, 8 + this.bytes[0]));
    },
    get byteLength() {
      var idLength = this.bytes[0];
      return 8 + idLength + idLength % 2;
    },
  };

  return {
    getStructView: function(segment) {
      switch (segment.getTypeParameter('which')) {
        case 'volume-descriptor': return VolumeDescriptorView;
        case 'path-table-entry': return PathTableEntryView;
        case 'boot-record': return BootRecordView;
        case 'volume-record': return VolumeRecordView;
        case 'folder': return DirectoryRecordView;
        case 'file': return DirectoryRecordView;
        default: return null;
      }
    },
    PathTableEntryView: PathTableEntryView,
    VolumeDescriptorView: VolumeDescriptorView,
    BootRecordView: BootRecordView,
    VolumeRecordView: VolumeRecordView,
    DirectoryRecordView: DirectoryRecordView,
  };

});
<commit_msg>Add sizeof_rootDirectory<commit_after>define(function() {

  'use strict';

  function VolumeDescriptorView(buffer, byteOffset, byteLength) {
    this.dv = new DataView(buffer, byteOffset, byteLength);
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
  }
  VolumeDescriptorView.prototype = {
    get typeCode() {
      return this.bytes[0];
    },
    get descriptorType() {
      switch (this.typeCode) {
        case 0: return 'boot';
        case 1: return 'volume'; // primary
        case 2: return 'volume'; // supplementary
        case 3: return 'partition';
        case 255: return 'terminator';
        default: return this.typeCode;
      }
    },
    get isPrimaryVolume() {
      return this.typeCode === 1;
    },
    get signature() {
      return String.fromCharCode(this.bytes[1], this.bytes[2], this.bytes[3], this.bytes[4], this.bytes[5]);
    },
    get hasValidSignature() {
      return this.signature === 'CD001';
    },
    get version() {
      return this.bytes[6];
    },
    get body() {
      var body, buffer = this.dv.buffer, byteOffset = this.dv.byteOffset, byteLength = this.dv.byteLength;
      switch(this.descriptorType) {
        case 'boot': body = new BootRecordView(buffer, byteOffset, byteLength); break;
        case 'volume': body = new VolumeRecordView(buffer, byteOffset, byteLength); break;
        default: body = null; break;
      }
      Object.defineProperty(this, 'body', {value:body});
      return body;
    },
  };

  function readSpacePadded(bytes) {
    return String.fromCharCode.apply(null, bytes).replace(/\s*$/, '');
  }

  function decodeTimeZoneSuffix(tz) {
    tz = (tz - 48) * 15;
    var sign = (tz < 0) ? ('-') : ('+');
    tz = Math.abs(tz);
    return sign + ('0' + Math.floor(tz / 60)).slice(-2) + ':' + ('0' + tz%60).slice(-2);
  }

  function readStringDateTime(bytes, offset) {
    var year = String.fromCharCode(bytes[offset + 0], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);
    if (!/^0*[1-9][0-9]*$/.test(year)) return null;
    var month = String.fromCharCode(bytes[offset + 4], bytes[offset + 5]);
    var day = String.fromCharCode(bytes[offset + 6], bytes[offset + 7]);
    var hour = String.fromCharCode(bytes[offset + 8], bytes[offset + 9]);
    var minute = String.fromCharCode(bytes[offset + 10], bytes[offset + 11]);
    var second = String.fromCharCode(bytes[offset + 12], bytes[offset + 13]);
    var hundredths = String.fromCharCode(bytes[offset + 14], bytes[offset + 15]);
    var timeZone = decodeTimeZoneSuffix(bytes[offset + 16]);
    var dateString = year+'-'+month+'-'+day+'T'+hour+':'+minute+':'+second+'.'+hundredths+timeZone;
    console.log(dateString);
    return new Date(dateString);
  }

  function readBinaryDateTime(bytes, offset) {
    if (bytes[offset + 1] === 0) return null; // month, must be 1-12
    var year = '' + (1900 + bytes[offset]);
    var month = ('0' + bytes[offset + 1]).slice(-2);
    var day = ('0' + bytes[offset + 2]).slice(-2);
    var hour = ('0' + bytes[offset + 3]).slice(-2);
    var minute = ('0' + bytes[offset + 4]).slice(-2);
    var second = ('0' + bytes[offset + 5]).slice(-2);
    var timeZone = decodeTimeZoneSuffix(bytes[offset + 6]);
    var dateString = year + '-' + month + '-' + day + 'T' + hour + ':' + minute + ':' + second + timeZone;
    console.log(dateString);
    return new Date(dateString);
  }

  function BootRecordView(buffer, byteOffset, byteLength) {
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
    this.bootData = this.bytes.subarray(71);
  }
  BootRecordView.prototype = {
    get systemIdentifier() {
      return readSpacePadded(this.bytes.subarray(7, 32));
    },
    get identifier() {
      return readSpacePadded(this.bytes.subarray(39, 64));
    },
  };

  function VolumeRecordView(buffer, byteOffset, byteLength) {
    this.dv = new DataView(buffer, byteOffset, byteLength);
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
  }
  VolumeRecordView.prototype = {
    get systemIdentifier() {
      return readSpacePadded(this.bytes.subarray(8, 32));
    },
    get identifier() {
      return readSpacePadded(this.bytes.subarray(40, 64));
    },
    get blockCount() {
      return this.dv.getUint32(80, true);
    },
    get diskCount() {
      return this.dv.getUint16(120, true);
    },
    get diskNumber() {
      return this.dv.getUint16(124, true);
    },
    get blockByteLength() {
      return this.dv.getUint16(128, true);
    },
    get pathTableByteLength() {
      return this.dv.getUint32(132, true);
    },
    get pathTableBlockAddressLE() {
      return this.dv.getUint32(140, true);
    },
    get optionalPathTableBlockAddressLE() {
      return this.dv.getUint32(144, true);
    },
    get pathTableBlockAddressLE() {
      return this.dv.getUint32(148, false);
    },
    get optionalPathTableBlockAddressLE() {
      return this.dv.getUint32(152, false);
    },
    get offsetof_rootDirectory() {
      return 156;
    },
    get sizeof_rootDirectory() {
      return 34;
    },
    get rootDirectory() {
      var root = new DirectoryRecordView(this.dv.buffer, this.dv.byteOffset + 156, 34);
      Object.defineProperty(this, 'rootDirectory', {value:root});
      return root;
    },
    get setIdentifier() {
      return readStringPadded(this.bytes.subarray(190, 190 + 128));
    },
    get publisher() {
      return readStringPadded(this.bytes.subarray(318, 318 + 128));
    },
    get dataPreparer() {
      return readStringPadded(this.bytes.subarray(446, 446 + 128));
    },
    get application() {
      return readStringPadded(this.bytes.subarray(574, 574 + 128));
    },
    get copyrightFilename() {
      return readStringPadded(this.bytes.subarray(702, 702 + 38));
    },
    get abstractFilename() {
      return readStringPadded(this.bytes.subarray(740, 740 + 36));
    },
    get bibliographyFilename() {
      return readStringPadded(this.bytes.subarray(776, 776 + 37));
    },
    get createdAt() {
      return readStringDateTime(this.bytes, 813);
    },
    get modifiedAt() {
      return readStringDateTime(this.bytes, 830);
    },
    get becomesObsoleteAt() {
      return readStringDateTime(this.bytes, 847);
    },
    get becomesEffectiveAt() {
      return readStringDateTime(this.bytes, 864);
    },
    get fileStructureVersion() {
      return this.bytes[881];
    },
    get applicationData() {
      var section = this.bytes.subarray(883, 883 + 512);
      Object.defineProperty(this, 'applicationData', {value:section});
      return section;
    },
  };

  function DirectoryRecordView(buffer, byteOffset, byteLength) {
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
    this.dv = new DataView(buffer, byteOffset, byteLength);
  }
  DirectoryRecordView.prototype = {
    get length() {
      return this.bytes[0];
    },
    get extendedAttributeRecordLength() {
      return this.bytes[1];
    },
    get dataBlockAddress() {
      return this.dv.getUint32(2, true);
    },
    get dataByteLength() {
      return this.dv.getUint32(10, true);
    },
    get recordedAt() {
      return readBinaryDateTime(this.bytes, 18);
    },
    get flags() {
      return this.bytes[25];
    },
    get isHidden() {
      return !!(this.flags & (1 << 0));
    },
    get isDirectory() {
      return !!(this.flags & (1 << 1));
    },
    get isAssociatedFile() {
      return !!(this.flags & (1 << 2));
    },
    get hasExtendedAttributeInfo() {
      return !!(this.flags & (1 << 3));
    },
    get hasPermissionsInExtendedAttributeInfo() {
      return !!(this.flags & (1 << 4));
    },
    get isPartial() {
      return !!(this.flags & (1 << 7));
    },
    get interleavedUnitSize() {
      return this.bytes[26];
    },
    get interleavedGapSize() {
      return this.bytes[27];
    },
    get volumeNumber() {
      return this.dv.getUint16(28, true);
    },
    get nameAndIdentifier() {
      return String.fromCharCode.apply(null, this.bytes.subarray(33, 33 + this.bytes[32]));
    },
    get name() {
      return this.nameAndIdentifier.replace(/;\d+$/, '');
    },
    get identifier() {
      return +(this.nameAndIdentifier.match(/;(\d+)$/) || '')[1];
    },
    get after_identifier() {
      var identifierLength = this.bytes[32];
      return 33 + identifierLength + ((identifierLength + 1) % 2);
    },
    get byteLength() {
      var extendedLength = this.extendedAttributeRecordLength;
      return this.after_identifier + extendedLength + extendedLength % 2;
    },
  };

  function PathTableEntryView(littleEndian, buffer, byteOffset, byteLength) {
    this.littleEndian = littleEndian;
    this.bytes = new Uint8Array(buffer, byteOffset, byteLength);
    this.dv = new DataView(buffer, byteOffset, byteLength);
  }
  PathTableEntryView.prototype = {
    get extendedAttributeLength() {
      return this.bytes[1];
    },
    get extentBlockAddress() {
      return this.dv.getUint32(2, this.littleEndian);
    },
    get parentDirectoryIndex() {
      return this.dv.getUint16(6, this.littleEndian);
    },
    get identifier() {
      return String.fromCharCode.apply(null, this.bytes.subarray(8, 8 + this.bytes[0]));
    },
    get byteLength() {
      var idLength = this.bytes[0];
      return 8 + idLength + idLength % 2;
    },
  };

  return {
    getStructView: function(segment) {
      switch (segment.getTypeParameter('which')) {
        case 'volume-descriptor': return VolumeDescriptorView;
        case 'path-table-entry': return PathTableEntryView;
        case 'boot-record': return BootRecordView;
        case 'volume-record': return VolumeRecordView;
        case 'folder': return DirectoryRecordView;
        case 'file': return DirectoryRecordView;
        default: return null;
      }
    },
    PathTableEntryView: PathTableEntryView,
    VolumeDescriptorView: VolumeDescriptorView,
    BootRecordView: BootRecordView,
    VolumeRecordView: VolumeRecordView,
    DirectoryRecordView: DirectoryRecordView,
  };

});
<|endoftext|>


<commit_before>var EventEmitter = require('events');
var Sequencer = require('./sequencer');
var Thread = require('./thread');
var util = require('util');

var defaultBlockPackages = {
    'scratch3': require('../blocks/scratch3'),
    'wedo2': require('../blocks/wedo2')
};

/**
 * Manages blocks, stacks, and the sequencer.
 */
function Runtime () {
    // Bind event emitter
    EventEmitter.call(this);

    // State for the runtime
    /**
     * All blocks in the workspace.
     * Keys are block IDs, values are metadata about the block.
     * @type {Object.<string, Object>}
     */
    this.blocks = {};

    /**
     * All stacks in the workspace.
     * A list of block IDs that represent stacks (first block in stack).
     * @type {Array.<String>}
     */
    this.stacks = [];

    /**
     * A list of threads that are currently running in the VM.
     * Threads are added when execution starts and pruned when execution ends.
     * @type {Array.<Thread>}
     */
    this.threads = [];

    /** @type {!Sequencer} */
    this.sequencer = new Sequencer(this);

    /**
     * Map to look up a block primitive's implementation function by its opcode.
     * This is a two-step lookup: package name first, then primitive name.
     * @type {Object.<string, Function>}
     */
    this._primitives = {};
    this._registerBlockPackages();
}

/**
 * Event name for glowing a stack
 * @const {string}
 */
Runtime.STACK_GLOW_ON = 'STACK_GLOW_ON';

/**
 * Event name for unglowing a stack
 * @const {string}
 */
Runtime.STACK_GLOW_OFF = 'STACK_GLOW_OFF';

/**
 * Event name for glowing a block
 * @const {string}
 */
Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON';

/**
 * Event name for unglowing a block
 * @const {string}
 */
Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF';

/**
 * Inherit from EventEmitter
 */
util.inherits(Runtime, EventEmitter);

/**
 * How rapidly we try to step threads, in ms.
 */
Runtime.THREAD_STEP_INTERVAL = 1000 / 30;

/**
 * Block management: create blocks and stacks from a `create` event
 * @param {!Object} block Blockly create event to be processed
 */
Runtime.prototype.createBlock = function (block, opt_isFlyoutBlock) {
    // Create new block
    this.blocks[block.id] = block;

    // Walk each field and add any shadow blocks
    // @todo Expand this to cover vertical / nested blocks
    for (var i in block.fields) {
        var shadows = block.fields[i].blocks;
        for (var y in shadows) {
            var shadow = shadows[y];
            this.blocks[shadow.id] = shadow;
        }
    }

    // Push block id to stacks array. New blocks are always a stack even if only
    // momentary. If the new block is added to an existing stack this stack will
    // be removed by the `moveBlock` method below.
    if (!opt_isFlyoutBlock) {
        this.stacks.push(block.id);
    }
};

/**
 * Block management: change block field values
 * @param {!Object} args Blockly change event to be processed
 */
Runtime.prototype.changeBlock = function (args) {
    // Validate
    if (args.element !== 'field') return;
    if (typeof this.blocks[args.id] === 'undefined') return;
    if (typeof this.blocks[args.id].fields[args.name] === 'undefined') return;

    // Update block value
    this.blocks[args.id].fields[args.name].value = args.value;
};

/**
 * Block management: move blocks from parent to parent
 * @param {!Object} e Blockly move event to be processed
 */
Runtime.prototype.moveBlock = function (e) {
    var _this = this;

    // Block was removed from parent
    if (e.newParent === undefined && e.oldParent !== undefined) {
        // Add stack
        _this.stacks.push(e.id);

        // Update old parent
        if (e.oldField === undefined) {
            _this.blocks[e.oldParent].next = null;
        } else {
            delete _this.blocks[e.oldParent].fields[e.oldField];
        }
    } else if (e.newParent !== undefined) {
        // Block was moved to a new parent
        // Either happens because it was previously parentless
        // (e.oldParent === undefined)
        // or because a block was moved in front of it.

        // Remove stack
        _this._deleteStack(e.id);

        // Update new parent
        if (e.newField === undefined) {
            _this.blocks[e.newParent].next = e.id;
        } else {
            _this.blocks[e.newParent].fields[e.newField] = {
                name: e.newField,
                value: e.id,
                blocks: {}
            };
        }
    }
};

/**
 * Block management: delete blocks and their associated stacks
 * @param {!Object} e Blockly delete event to be processed
 */
Runtime.prototype.deleteBlock = function (e) {
    // @todo Stop threads running on this stack

    // Get block
    var block = this.blocks[e.id];

    // Delete children
    if (block.next !== null) {
        this.deleteBlock({id: block.next});
    }

    // Delete substacks and fields
    for (var field in block.fields) {
        if (field === 'SUBSTACK') {
            this.deleteBlock({id: block.fields[field].value});
        } else {
            for (var shadow in block.fields[field].blocks) {
                this.deleteBlock({id: shadow});
            }
        }
    }

    // Delete stack
    this._deleteStack(e.id);

    // Delete block
    delete this.blocks[e.id];
};

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
 * Register default block packages with this runtime.
 * @todo Prefix opcodes with package name.
 * @private
 */
Runtime.prototype._registerBlockPackages = function () {
    for (var packageName in defaultBlockPackages) {
        if (defaultBlockPackages.hasOwnProperty(packageName)) {
            // @todo pass a different runtime depending on package privilege?
            var packageObject = new (defaultBlockPackages[packageName])(this);
            var packageContents = packageObject.getPrimitives();
            for (var op in packageContents) {
                if (packageContents.hasOwnProperty(op)) {
                    this._primitives[op] =
                        packageContents[op].bind(packageObject);
                }
            }
        }
    }
};

/**
 * Retrieve the function associated with the given opcode.
 * @param {!string} opcode The opcode to look up.
 * @return {Function} The function which implements the opcode.
 */
Runtime.prototype.getOpcodeFunction = function (opcode) {
    return this._primitives[opcode];
};

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
 * Create a thread and push it to the list of threads.
 * @param {!string} id ID of block that starts the stack
 */
Runtime.prototype._pushThread = function (id) {
    this.emit(Runtime.STACK_GLOW_ON, id);
    var thread = new Thread(id);
    this.threads.push(thread);
};

/**
 * Remove a thread from the list of threads.
 * @param {?Thread} thread Thread object to remove from actives
 */
Runtime.prototype._removeThread = function (thread) {
    var i = this.threads.indexOf(thread);
    if (i > -1) {
        this.emit(Runtime.STACK_GLOW_OFF, thread.topBlock);
        this.threads.splice(i, 1);
    }
};

/**
 * Toggle a stack
 * @param {!string} stackId ID of block that starts the stack
 */
Runtime.prototype.toggleStack = function (stackId) {
    // Remove any existing thread
    for (var i = 0; i < this.threads.length; i++) {
        if (this.threads[i].topBlock == stackId) {
            this._removeThread(this.threads[i]);
            return;
        }
    }
    // Otherwise add it
    this._pushThread(stackId);
};

/**
 * Green flag, which stops currently running threads
 * and adds all top-level stacks that start with the green flag
 */
Runtime.prototype.greenFlag = function () {
    // Remove all existing threads
    for (var i = 0; i < this.threads.length; i++) {
        this._removeThread(this.threads[i]);
    }
    // Add all top stacks with green flag
    for (var j = 0; j < this.stacks.length; j++) {
        var topBlock = this.stacks[j];
        if (this.blocks[topBlock].opcode === 'event_whenflagclicked') {
            this._pushThread(this.stacks[j]);
        }
    }
};

/**
 * Distance sensor hack
 */
Runtime.prototype.startDistanceSensors = function () {
    // Add all top stacks with distance sensor
    for (var j = 0; j < this.stacks.length; j++) {
        var topBlock = this.stacks[j];
        if (this.blocks[topBlock].opcode === 'wedo_whendistanceclose') {
            var alreadyRunning = false;
            for (var k = 0; k < this.threads.length; k++) {
                if (this.threads[k].topBlock === topBlock) {
                    alreadyRunning = true;
                }
            }
            if (!alreadyRunning) {
                this._pushThread(this.stacks[j]);
            }
        }
    }
};

/**
 * Stop "everything"
 */
Runtime.prototype.stopAll = function () {
    var threadsCopy = this.threads.slice();
    while (threadsCopy.length > 0) {
        this._removeThread(threadsCopy.pop());
    }
    // @todo call stop function in all extensions/packages/WeDo stub
    if (window.native) {
        window.native.motorStop();
    }
};

/**
 * Repeatedly run `sequencer.stepThreads` and filter out
 * inactive threads after each iteration.
 */
Runtime.prototype._step = function () {
    var inactiveThreads = this.sequencer.stepThreads(this.threads);
    for (var i = 0; i < inactiveThreads.length; i++) {
        this._removeThread(inactiveThreads[i]);
    }
};

/**
 * Emit feedback for block glowing (used in the sequencer).
 * @param {?string} blockId ID for the block to update glow
 * @param {boolean} isGlowing True to turn on glow; false to turn off.
 */
Runtime.prototype.glowBlock = function (blockId, isGlowing) {
    if (isGlowing) {
        this.emit(Runtime.BLOCK_GLOW_ON, blockId);
    } else {
        this.emit(Runtime.BLOCK_GLOW_OFF, blockId);
    }
};

/**
 * Set up timers to repeatedly step in a browser
 */
Runtime.prototype.start = function () {
    if (!window.setInterval) return;
    window.setInterval(function() {
        this._step();
    }.bind(this), Runtime.THREAD_STEP_INTERVAL);
};

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
 * Helper to remove a stack from `this.stacks`
 * @param {?string} id ID of block that starts the stack
 */
Runtime.prototype._deleteStack = function (id) {
    var i = this.stacks.indexOf(id);
    if (i > -1) this.stacks.splice(i, 1);
};

/**
 * Helper to get the next block for a particular block
 * @param {?string} id ID of block to get the next block for
 * @return {?string} ID of next block in the sequence
 */
Runtime.prototype._getNextBlock = function (id) {
    if (typeof this.blocks[id] === 'undefined') return null;
    return this.blocks[id].next;
};

/**
 * Helper to get the substack for a particular C-shaped block
 * @param {?string} id ID for block to get the substack for
 * @return {?string} ID of block in the substack
 */
Runtime.prototype._getSubstack = function (id) {
    if (typeof this.blocks[id] === 'undefined') return null;
    return this.blocks[id].fields['SUBSTACK'];
};

/**
 * Helper to get the opcode for a particular block
 * @param {?string} id ID of block to query
 * @return {?string} the opcode corresponding to that block
 */
Runtime.prototype._getOpcode = function (id) {
    if (typeof this.blocks[id] === 'undefined') return null;
    return this.blocks[id].opcode;
};

module.exports = Runtime;
<commit_msg>Update Runtime createBlock for new structure; _addStack helper.<commit_after>var EventEmitter = require('events');
var Sequencer = require('./sequencer');
var Thread = require('./thread');
var util = require('util');

var defaultBlockPackages = {
    'scratch3': require('../blocks/scratch3'),
    'wedo2': require('../blocks/wedo2')
};

/**
 * Manages blocks, stacks, and the sequencer.
 */
function Runtime () {
    // Bind event emitter
    EventEmitter.call(this);

    // State for the runtime
    /**
     * All blocks in the workspace.
     * Keys are block IDs, values are metadata about the block.
     * @type {Object.<string, Object>}
     */
    this.blocks = {};

    /**
     * All stacks in the workspace.
     * A list of block IDs that represent stacks (first block in stack).
     * @type {Array.<String>}
     */
    this.stacks = [];

    /**
     * A list of threads that are currently running in the VM.
     * Threads are added when execution starts and pruned when execution ends.
     * @type {Array.<Thread>}
     */
    this.threads = [];

    /** @type {!Sequencer} */
    this.sequencer = new Sequencer(this);

    /**
     * Map to look up a block primitive's implementation function by its opcode.
     * This is a two-step lookup: package name first, then primitive name.
     * @type {Object.<string, Function>}
     */
    this._primitives = {};
    this._registerBlockPackages();
}

/**
 * Event name for glowing a stack
 * @const {string}
 */
Runtime.STACK_GLOW_ON = 'STACK_GLOW_ON';

/**
 * Event name for unglowing a stack
 * @const {string}
 */
Runtime.STACK_GLOW_OFF = 'STACK_GLOW_OFF';

/**
 * Event name for glowing a block
 * @const {string}
 */
Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON';

/**
 * Event name for unglowing a block
 * @const {string}
 */
Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF';

/**
 * Inherit from EventEmitter
 */
util.inherits(Runtime, EventEmitter);

/**
 * How rapidly we try to step threads, in ms.
 */
Runtime.THREAD_STEP_INTERVAL = 1000 / 30;

/**
 * Block management: create blocks and stacks from a `create` event
 * @param {!Object} block Blockly create event to be processed
 */
Runtime.prototype.createBlock = function (block, opt_isFlyoutBlock) {
    // Create new block
    this.blocks[block.id] = block;

    // Push block id to stacks array. New blocks are always a stack even if only
    // momentary. If the new block is added to an existing stack this stack will
    // be removed by the `moveBlock` method below.
    if (!opt_isFlyoutBlock) {
        this._addStack(block.id);
    }
};

/**
 * Block management: change block field values
 * @param {!Object} args Blockly change event to be processed
 */
Runtime.prototype.changeBlock = function (args) {
    // Validate
    if (args.element !== 'field') return;
    if (typeof this.blocks[args.id] === 'undefined') return;
    if (typeof this.blocks[args.id].fields[args.name] === 'undefined') return;

    // Update block value
    this.blocks[args.id].fields[args.name].value = args.value;
};

/**
 * Block management: move blocks from parent to parent
 * @param {!Object} e Blockly move event to be processed
 */
Runtime.prototype.moveBlock = function (e) {
    var _this = this;

    // Block was removed from parent
    if (e.newParent === undefined && e.oldParent !== undefined) {
        _this.addStack(e.id);

        // Update old parent
        if (e.oldField === undefined) {
            _this.blocks[e.oldParent].next = null;
        } else {
            delete _this.blocks[e.oldParent].fields[e.oldField];
        }
    } else if (e.newParent !== undefined) {
        // Block was moved to a new parent
        // Either happens because it was previously parentless
        // (e.oldParent === undefined)
        // or because a block was moved in front of it.

        // Remove stack
        _this._deleteStack(e.id);

        // Update new parent
        if (e.newField === undefined) {
            _this.blocks[e.newParent].next = e.id;
        } else {
            _this.blocks[e.newParent].fields[e.newField] = {
                name: e.newField,
                value: e.id,
                blocks: {}
            };
        }
    }
};

/**
 * Block management: delete blocks and their associated stacks
 * @param {!Object} e Blockly delete event to be processed
 */
Runtime.prototype.deleteBlock = function (e) {
    // @todo Stop threads running on this stack

    // Get block
    var block = this.blocks[e.id];

    // Delete children
    if (block.next !== null) {
        this.deleteBlock({id: block.next});
    }

    // Delete substacks and fields
    for (var field in block.fields) {
        if (field === 'SUBSTACK') {
            this.deleteBlock({id: block.fields[field].value});
        } else {
            for (var shadow in block.fields[field].blocks) {
                this.deleteBlock({id: shadow});
            }
        }
    }

    // Delete stack
    this._deleteStack(e.id);

    // Delete block
    delete this.blocks[e.id];
};

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
 * Register default block packages with this runtime.
 * @todo Prefix opcodes with package name.
 * @private
 */
Runtime.prototype._registerBlockPackages = function () {
    for (var packageName in defaultBlockPackages) {
        if (defaultBlockPackages.hasOwnProperty(packageName)) {
            // @todo pass a different runtime depending on package privilege?
            var packageObject = new (defaultBlockPackages[packageName])(this);
            var packageContents = packageObject.getPrimitives();
            for (var op in packageContents) {
                if (packageContents.hasOwnProperty(op)) {
                    this._primitives[op] =
                        packageContents[op].bind(packageObject);
                }
            }
        }
    }
};

/**
 * Retrieve the function associated with the given opcode.
 * @param {!string} opcode The opcode to look up.
 * @return {Function} The function which implements the opcode.
 */
Runtime.prototype.getOpcodeFunction = function (opcode) {
    return this._primitives[opcode];
};

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
 * Create a thread and push it to the list of threads.
 * @param {!string} id ID of block that starts the stack
 */
Runtime.prototype._pushThread = function (id) {
    this.emit(Runtime.STACK_GLOW_ON, id);
    var thread = new Thread(id);
    this.threads.push(thread);
};

/**
 * Remove a thread from the list of threads.
 * @param {?Thread} thread Thread object to remove from actives
 */
Runtime.prototype._removeThread = function (thread) {
    var i = this.threads.indexOf(thread);
    if (i > -1) {
        this.emit(Runtime.STACK_GLOW_OFF, thread.topBlock);
        this.threads.splice(i, 1);
    }
};

/**
 * Toggle a stack
 * @param {!string} stackId ID of block that starts the stack
 */
Runtime.prototype.toggleStack = function (stackId) {
    // Remove any existing thread
    for (var i = 0; i < this.threads.length; i++) {
        if (this.threads[i].topBlock == stackId) {
            this._removeThread(this.threads[i]);
            return;
        }
    }
    // Otherwise add it
    this._pushThread(stackId);
};

/**
 * Green flag, which stops currently running threads
 * and adds all top-level stacks that start with the green flag
 */
Runtime.prototype.greenFlag = function () {
    // Remove all existing threads
    for (var i = 0; i < this.threads.length; i++) {
        this._removeThread(this.threads[i]);
    }
    // Add all top stacks with green flag
    for (var j = 0; j < this.stacks.length; j++) {
        var topBlock = this.stacks[j];
        if (this.blocks[topBlock].opcode === 'event_whenflagclicked') {
            this._pushThread(this.stacks[j]);
        }
    }
};

/**
 * Distance sensor hack
 */
Runtime.prototype.startDistanceSensors = function () {
    // Add all top stacks with distance sensor
    for (var j = 0; j < this.stacks.length; j++) {
        var topBlock = this.stacks[j];
        if (this.blocks[topBlock].opcode === 'wedo_whendistanceclose') {
            var alreadyRunning = false;
            for (var k = 0; k < this.threads.length; k++) {
                if (this.threads[k].topBlock === topBlock) {
                    alreadyRunning = true;
                }
            }
            if (!alreadyRunning) {
                this._pushThread(this.stacks[j]);
            }
        }
    }
};

/**
 * Stop "everything"
 */
Runtime.prototype.stopAll = function () {
    var threadsCopy = this.threads.slice();
    while (threadsCopy.length > 0) {
        this._removeThread(threadsCopy.pop());
    }
    // @todo call stop function in all extensions/packages/WeDo stub
    if (window.native) {
        window.native.motorStop();
    }
};

/**
 * Repeatedly run `sequencer.stepThreads` and filter out
 * inactive threads after each iteration.
 */
Runtime.prototype._step = function () {
    var inactiveThreads = this.sequencer.stepThreads(this.threads);
    for (var i = 0; i < inactiveThreads.length; i++) {
        this._removeThread(inactiveThreads[i]);
    }
};

/**
 * Emit feedback for block glowing (used in the sequencer).
 * @param {?string} blockId ID for the block to update glow
 * @param {boolean} isGlowing True to turn on glow; false to turn off.
 */
Runtime.prototype.glowBlock = function (blockId, isGlowing) {
    if (isGlowing) {
        this.emit(Runtime.BLOCK_GLOW_ON, blockId);
    } else {
        this.emit(Runtime.BLOCK_GLOW_OFF, blockId);
    }
};

/**
 * Set up timers to repeatedly step in a browser
 */
Runtime.prototype.start = function () {
    if (!window.setInterval) return;
    window.setInterval(function() {
        this._step();
    }.bind(this), Runtime.THREAD_STEP_INTERVAL);
};

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

/**
 * Helper to add a stack to `this.stacks`
 * @param {?string} id ID of block that starts the stack
 */
Runtime.prototype._addStack = function (id) {
    var i = this.stacks.indexOf(id);
    if (i > -1) return; // Already in stacks.
    this.stacks.push(id);
    // Update `topLevel` property on the top block.
    this.blocks[id].topLevel = true;
};

/**
 * Helper to remove a stack from `this.stacks`
 * @param {?string} id ID of block that starts the stack
 */
Runtime.prototype._deleteStack = function (id) {
    var i = this.stacks.indexOf(id);
    if (i > -1) {
        this.stacks.splice(i, 1);
        // Update `topLevel` property on the top block.
        this.blocks[id].topLevel = false;
    }
};

/**
 * Helper to get the next block for a particular block
 * @param {?string} id ID of block to get the next block for
 * @return {?string} ID of next block in the sequence
 */
Runtime.prototype._getNextBlock = function (id) {
    if (typeof this.blocks[id] === 'undefined') return null;
    return this.blocks[id].next;
};

/**
 * Helper to get the substack for a particular C-shaped block
 * @param {?string} id ID for block to get the substack for
 * @return {?string} ID of block in the substack
 */
Runtime.prototype._getSubstack = function (id) {
    if (typeof this.blocks[id] === 'undefined') return null;
    return this.blocks[id].fields['SUBSTACK'];
};

/**
 * Helper to get the opcode for a particular block
 * @param {?string} id ID of block to query
 * @return {?string} the opcode corresponding to that block
 */
Runtime.prototype._getOpcode = function (id) {
    if (typeof this.blocks[id] === 'undefined') return null;
    return this.blocks[id].opcode;
};

module.exports = Runtime;
<|endoftext|>


<commit_before>! function() {

  var model = raw.model();

  // 'Dimensions' dimension. accept multiple values.
  // Each value represent a slice of the pie.
  var dimensions = model.dimension()
    .title('Dimensions')
    .required(true)
    .multiple(true);

  // Group dimension. It will mainly provide a label for each
  // chart. If multiple lines share the same value in the
  // group dimension, they will be grouped.
  var group = model.dimension()
    .title('Group');

  // Mapping function.
  // For each record in the dataset a pie chart abstraction is created.
  // Records are grouped according the 'group' variable.

  model.map(function(data) {
    var index = 0;
    var nest = d3.nest()
      // If groups are not defined, assign a number to each record.
      .key(function(d) {
        return group() ? group(d) : ++index; })
      .rollup(function(d) {
        return dimensions().map(function(dimension) {
          return { key: dimension, size: d3.sum(d, function(a) {
              return +a[dimension]; }) }
        })
      })
      .entries(data);

    return nest;
  })

  var chart = raw.chart()
    .title("Pie chart")
    .model(model);

  var width = chart.number()
    .title('Width')
    .defaultValue(800)

  var columns = chart.number()
    .title('Columns')
    .defaultValue(4)

  var padding = chart.number()
    .title('Padding')
    .defaultValue(10)

  var donut = chart.checkbox()
    .title('Donut chart')
    .defaultValue(false)

  var thickness = chart.number()
    .title('Thickness')
    .defaultValue(10)

  var sortChartsBy = chart.list()
    .title("Sort items by")
    .values(['size', 'name', 'automatic'])
    .defaultValue('size')

  var sortArcsBy = chart.list()
    .title("Sort arcs by")
    .values(['size', 'name', 'automatic'])
    .defaultValue('size')

  // Chart colors
  var colors = chart.color()
    .title("Color scale")

  // Drawing function.

  chart.draw(function(selection, data) {

    var radius = +width() / +columns() / 2 - +padding() / 2;

    // Define color scale domain
    // Get the list of all possible values from first element
    // Use it to define the colors domain
    var allColors = data[0].values.map(function(item){return item.key});
    colors.domain(allColors);

    var h = Math.ceil(data.length / +columns()) * (radius * 2 + padding());

    selection
      .attr("width", +width())
      .attr("height", h)

    var area = d3.scale.linear()
      .domain([0, d3.max(data, function(layer) {
        return d3.sum(layer.values, function(d) {
          return d.size; }); })])
      .range([0, radius * radius * Math.PI])

    // Sort pie slices according to the 'sortArcsBy' function
    var pie = d3.layout.pie()
      .sort(sortArcsByComparator)
      .value(function(d) {
        return d.size; });

    // Sort data according to the 'sortChartsBy' function
    data.sort(sortChartsByComparator);

    data.forEach(function(l, li) {

      var outerRadius = Math.sqrt(area(d3.sum(l.values, function(d) {
        return d.size; })) / Math.PI);

      var arc = d3.svg.arc()
        .outerRadius(outerRadius)
        .innerRadius(donut() && thickness() < outerRadius ? outerRadius - +thickness() : 0)

      var g = selection
        .append("g")
        .attr("transform", function() {
          return "translate(" + (li % +columns() * (+radius * 2 + +padding()) + +radius + padding() / 2) + "," + (Math.floor(li / +columns()) * (radius * 2 + +padding()) + radius + padding() / 2) + ")";
        })

      var p = g.selectAll(".arc")
        .data(pie(l.values))
        .enter().append("g")
        .attr("class", "arc");

      p.append("path")
        .attr("d", arc)
        .style("fill", function(d) {
          return colors()(d.data.key); });

      /*p.append("text")
          .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
          .attr("dy", ".35em")
          .style("text-anchor", "middle")
          .text(function(d) { return d.data.key; });*/

      var label = g.selectAll(".label")
        .data(l.values)
        .enter().append("text")

      label.text(function(d) {
        return l.key })
      label.style("text-anchor", "middle")

    })

    function sortChartsByComparator(a, b) {
      if (sortChartsBy() == 'size') {
        return d3.descending(d3.sum(a.values, function(d) {
            return d.size; }),
          d3.sum(b.values, function(d) {
            return d.size; }));
      }
      if (sortChartsBy() == 'name') return d3.ascending(a.key, b.key);
      if (sortChartsBy() == 'automatic') return 1;

    }

    function sortArcsByComparator(a, b) {
      if (sortArcsBy() == 'size') return d3.ascending(a.size, b.size);
      if (sortArcsBy() == 'name') return d3.descending(a.key, b.key);
      if (sortArcsBy() == 'automatic') return 1;
    }

  })

}()
<commit_msg>Added labels, fixed layout<commit_after>! function() {

  var model = raw.model();

  // 'Dimensions' dimension. accept multiple values.
  // Each value represent a slice of the pie.
  var dimensions = model.dimension()
    .title('Dimensions')
    .required(true)
    .multiple(true);

  // Group dimension. It will mainly provide a label for each
  // chart. If multiple lines share the same value in the
  // group dimension, they will be grouped.
  var group = model.dimension()
    .title('Group');

  // Mapping function.
  // For each record in the dataset a pie chart abstraction is created.
  // Records are grouped according the 'group' variable.

  model.map(function(data) {
    var index = 0;
    var nest = d3.nest()
      // If groups are not defined, assign a number to each record.
      .key(function(d) {
        return group() ? group(d) : ++index; })
      .rollup(function(d) {
        return dimensions().map(function(dimension) {
          return { key: dimension, size: d3.sum(d, function(a) {
              return +a[dimension]; }) }
        })
      })
      .entries(data);

    return nest;
  })

  var chart = raw.chart()
    .title("Pie chart")
    .model(model);

  var width = chart.number()
    .title('Width')
    .defaultValue(800)

  var columns = chart.number()
    .title('Columns')
    .defaultValue(4)

  var padding = chart.number()
    .title('Padding')
    .defaultValue(10)

  var donut = chart.checkbox()
    .title('Donut chart')
    .defaultValue(false)

  var showValues = chart.checkbox()
    .title('Show values')
    .defaultValue(false)

  var thickness = chart.number()
    .title('Thickness')
    .defaultValue(10)

  var sortChartsBy = chart.list()
    .title("Sort items by")
    .values(['size', 'name', 'automatic'])
    .defaultValue('size')

  var sortArcsBy = chart.list()
    .title("Sort arcs by")
    .values(['size', 'name', 'automatic'])
    .defaultValue('size')

  // Chart colors
  var colors = chart.color()
    .title("Color scale")

  // Drawing function.

  chart.draw(function(selection, data) {

    var radius = +width() / +columns() / 2 - +padding() / 2;

    // Define color scale domain
    // Get the list of all possible values from first element
    // Use it to define the colors domain
    var allColors = data[0].values.map(function(item){return item.key});
    colors.domain(allColors);

    var h = Math.ceil(data.length / +columns()) * (radius * 2 + padding());

    selection
      .attr("width", +width())
      .attr("height", h)

    var area = d3.scale.linear()
      .domain([0, d3.max(data, function(layer) {
        return d3.sum(layer.values, function(d) {
          return d.size; }); })])
      .range([0, radius * radius * Math.PI])

    // Sort pie slices according to the 'sortArcsBy' function
    var pie = d3.layout.pie()
      .sort(sortArcsByComparator)
      .value(function(d) {
        return d.size; });

    // Sort data according to the 'sortChartsBy' function
    data.sort(sortChartsByComparator);

    data.forEach(function(l, li) {

      var outerRadius = Math.sqrt(area(d3.sum(l.values, function(d) {
        return d.size; })) / Math.PI);

      var arc = d3.svg.arc()
        .outerRadius(outerRadius)
        .innerRadius(donut() && thickness() < outerRadius ? outerRadius - +thickness() : 0)

      var g = selection
        .append("g")
        .attr("transform", function() {
          return "translate(" + (li % +columns() * (+radius * 2 + +padding()) + +radius + padding() / 2) + "," + (Math.floor(li / +columns()) * (radius * 2 + +padding()) + radius + padding() / 2) + ")";
        })

      var p = g.selectAll(".arc")
        .data(pie(l.values))
        .enter().append("g")
        .attr("class", "arc");

      p.append("path")
        .attr("d", arc)
        .style("fill", function(d) {
          return colors()(d.data.key); });

      if(showValues() == true) {

        var labels = g.selectAll(".numbers")
            .data(pie(l.values))
            .enter().append("text")
            .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
            .attr("dy", ".35em")
            .style("text-anchor", "middle")
            .style("font-size","10px")
            .style("font-family","Arial, Helvetica")
            .style("fill", "#424242")
            .text(function(d) {return d.data.size.toLocaleString() });
      }

      var label = g.append("text")
        .attr("y", outerRadius+10)
        .text(l.key)
        .style("text-anchor", "middle")
        .style("font-size","11px")
        .style("font-family","Arial, Helvetica")

    })

    function sortChartsByComparator(a, b) {
      if (sortChartsBy() == 'size') {
        return d3.descending(d3.sum(a.values, function(d) {
            return d.size; }),
          d3.sum(b.values, function(d) {
            return d.size; }));
      }
      if (sortChartsBy() == 'name') return d3.ascending(a.key, b.key);
      if (sortChartsBy() == 'automatic') return 1;

    }

    function sortArcsByComparator(a, b) {
      if (sortArcsBy() == 'size') return d3.ascending(a.size, b.size);
      if (sortArcsBy() == 'name') return d3.descending(a.key, b.key);
      if (sortArcsBy() == 'automatic') return 1;
    }

  })

}()
<|endoftext|>


<commit_before>'use strict';

var toNatural            = require('es5-ext/number/to-pos-integer')
  , object               = require('es5-ext/object/valid-object')
  , value                = require('es5-ext/object/valid-value')
  , memoize              = require('memoizee/plain')
  , once                 = require('timers-ext/once')
  , ObservableValue      = require('observable-value')
  , ObservableSet        = require('observable-set')
  , ReactiveTable        = require('reactive-table')
  , ReactiveList         = require('reactive-table/list')
  , location             = require('mano/lib/client/location')
  , db                   = require('mano').db
  , fixLocationQuery     = require('../../utils/fix-location-query')
  , serializeSnapshotKey = require('../../utils/serialize-to-snapshot-key')
  , getFilter            = require('../../utils/get-users-filter')
  , Pagination           = require('./pagination')

  , ceil = Math.ceil, create = Object.create, keys = Object.keys;

var resolveUsers = function (value) {
	if (!value) return [];
	return value.split(',').map(function (id) {
		return db.objects.unserialize(id, db.User);
	});
};
var getUsersSnapshot = memoize(function (observable) {
	var set = new ObservableSet(resolveUsers(observable.value));
	observable.on('change', function (event) {
		set._postponed_ += 1;
		set.clear();
		resolveUsers(event.newValue).forEach(set.add, set);
		set._postponed_ -= 1;
	});
	return set;
}, { normalizer: function (args) { return args[0].dbId; } });

module.exports = function (snapshots, options) {
	var list, table, pagination, i18n, columns
	  , statusQuery, searchQuery, pathname, pageLimit, statusMap
	  , active, update, appName, pageQuery, inSync, isPartial, tableStyle;

	var getPageCount = function (value) {
		if (!value) return 1;
		return ceil(value / pageLimit);
	};
	object(options);
	columns = object(options.columns);
	i18n = options.i18n ? object(options.i18n) : create(null);
	appName = value(options.appName);
	pathname = value(options.pathname);
	value(options.cacheLimits);
	pageLimit = options.cacheLimits.usersPerPage;
	statusMap = object(options.users);
	inSync = new ObservableValue(true);
	isPartial = (function () {
		var snapshotData = { appName: appName };
		if (statusMap[i18n.all || 'all']) snapshotData.status = i18n.all || 'all';
		return db.User.dataSnapshots.get(serializeSnapshotKey(snapshotData))
			._totalSize.map(function (value) { return value > options.cacheLimits.listedUsers; });
	}());

	update = once(function () {
		var status, search, normalizedSearch, page, snapshot, usersSnapshot
		  , snapshotData, snapshotKey, maxPage, users;
		if (!active) return;
		snapshotData = { appName: appName };

		// Resolve status
		if (statusQuery) {
			if (statusQuery.value && !statusMap[statusQuery.value]) {
				fixLocationQuery(i18n.status || 'status');
			} else {
				status = statusQuery.value || '';
			}
			users = statusMap[status];
			if (status) snapshotData.status = status;
		} else {
			users = statusMap[''];
		}

		// Resolve search
		if (searchQuery) {
			search = searchQuery.value;
			if (search != null) {
				normalizedSearch = search.trim().toLowerCase();
				if (search !== normalizedSearch) {
					search = normalizedSearch;
					fixLocationQuery(i18n.search || 'search', search || null);
				}
			}
			if (search) {
				users = users.filter(getFilter(search));
				snapshotData.search = search;
			}
		}
		snapshot = db.User.dataSnapshots.get(serializeSnapshotKey(snapshotData));

		pagination.count.value = isPartial.value
			? snapshot._totalSize.map(getPageCount) : users._size.map(getPageCount);
		maxPage = pagination.count.value;

		// Resolve page
		if (pageQuery.value != null) {
			page = toNatural(pageQuery.value) || null;
			if (page === 1) page = null;
			if (page) {
				if (maxPage < page) page = (maxPage > 1) ? maxPage : null;
			}
			if (page) page = String(page);
			if (page !== pageQuery.value) fixLocationQuery(i18n.page || 'page', page);
			if (page) page = Number(page);
		}
		pagination.current.value = page || 1;

		// Update table
		if (isPartial.value) {
			// We rely on snapshots functionality
			snapshotData.page = page || 1;
			// Inform remote
			snapshotKey = serializeSnapshotKey(snapshotData);
			if (snapshots.last !== snapshotKey) snapshots.add(snapshotKey);
			if (page) {
				// Rely on remote prepared snapshot
				usersSnapshot = getUsersSnapshot(snapshot._get(page));
				inSync.value = usersSnapshot._size.gt(0);
				list.set = usersSnapshot;
				list.page = 1;
				return;
			}
			inSync.value = snapshot._totalSize.map(function (value) {
				if (value == null) return false;
				return users._size.gtOrEq(value > pageLimit ? pageLimit : value);
			});
		} else {
			// Assure that we have all data on board
			snapshotData = { appName: appName };
			if (statusMap[i18n.all || 'all']) snapshotData.status = i18n.all || 'all';
			snapshotKey = serializeSnapshotKey(snapshotData);
			if (snapshots.last !== snapshotKey) snapshots.add(snapshotKey);
		}
		list.set = users;
		list.page = page || 1;
		inSync.value = true;
	});

	// Setup
	// Status filter
	if (keys(statusMap).length > 1) {
		statusQuery = location.query.get(i18n.status || 'status');
		statusQuery.on('change', update);
	}

	// Search filter
	if (options.searchFilter) {
		searchQuery = location.query.get(i18n.search || 'search');
		searchQuery.on('change', update);
	}

	// Pagination
	pageQuery = location.query.get(i18n.page || 'page');
	pageQuery.on('change', update);

	// Table configuration
	list = new ReactiveList(statusMap[''], options.compare);
	list.limit = pageLimit;
	table = new ReactiveTable(document, list, columns);
	table.pagination = pagination = new Pagination(pathname);

	table.inSync = inSync;
	tableStyle = window.getComputedStyle(table.table);
	inSync.on('change', function (event) {
		table.table.style.height = event.newValue ? '' : tableStyle.height;
		table.table.classList[event.newValue ? 'remove' : 'add']('not-in-sync');
	});

	if (options.id) table.table.id = options.id;
	if (options.class) table.table.className = options.class;

	location.on('change', function () {
		active = (this.pathname === pathname);
		update();
	});
	active = location.pathname === pathname;

	isPartial.on('change', update);
	window.addEventListener('focus', update, false);

	update();
	return table;
};
<commit_msg>Rescue eventual race condition<commit_after>'use strict';

var toNatural            = require('es5-ext/number/to-pos-integer')
  , object               = require('es5-ext/object/valid-object')
  , value                = require('es5-ext/object/valid-value')
  , memoize              = require('memoizee/plain')
  , once                 = require('timers-ext/once')
  , ObservableValue      = require('observable-value')
  , ObservableSet        = require('observable-set')
  , ReactiveTable        = require('reactive-table')
  , ReactiveList         = require('reactive-table/list')
  , location             = require('mano/lib/client/location')
  , db                   = require('mano').db
  , fixLocationQuery     = require('../../utils/fix-location-query')
  , serializeSnapshotKey = require('../../utils/serialize-to-snapshot-key')
  , getFilter            = require('../../utils/get-users-filter')
  , Pagination           = require('./pagination')

  , ceil = Math.ceil, create = Object.create, keys = Object.keys;

var resolveUsers = function (value) {
	if (!value) return [];
	return value.split(',').map(function (id) {
		return db.objects.unserialize(id, db.User);
	});
};
var getUsersSnapshot = memoize(function (observable) {
	var set = new ObservableSet(resolveUsers(observable.value));
	observable.on('change', function (event) {
		set._postponed_ += 1;
		set.clear();
		resolveUsers(event.newValue).forEach(set.add, set);
		set._postponed_ -= 1;
	});
	return set;
}, { normalizer: function (args) { return args[0].dbId; } });

module.exports = function (snapshots, options) {
	var list, table, pagination, i18n, columns
	  , statusQuery, searchQuery, pathname, pageLimit, statusMap
	  , active, update, appName, pageQuery, inSync, isPartial, tableStyle, snapshotKey;

	var getPageCount = function (value) {
		if (!value) return 1;
		return ceil(value / pageLimit);
	};
	object(options);
	columns = object(options.columns);
	i18n = options.i18n ? object(options.i18n) : create(null);
	appName = value(options.appName);
	pathname = value(options.pathname);
	value(options.cacheLimits);
	pageLimit = options.cacheLimits.usersPerPage;
	statusMap = object(options.users);
	inSync = new ObservableValue(true);
	isPartial = (function () {
		var snapshotData = { appName: appName };
		if (statusMap[i18n.all || 'all']) snapshotData.status = i18n.all || 'all';
		return db.User.dataSnapshots.get(serializeSnapshotKey(snapshotData))
			._totalSize.map(function (value) { return value > options.cacheLimits.listedUsers; });
	}());

	update = once(function () {
		var status, search, normalizedSearch, page, snapshot, usersSnapshot
		  , snapshotData, maxPage, users;
		if (!active) return;
		snapshotData = { appName: appName };

		// Resolve status
		if (statusQuery) {
			if (statusQuery.value && !statusMap[statusQuery.value]) {
				fixLocationQuery(i18n.status || 'status');
			} else {
				status = statusQuery.value || '';
			}
			users = statusMap[status];
			if (status) snapshotData.status = status;
		} else {
			users = statusMap[''];
		}

		// Resolve search
		if (searchQuery) {
			search = searchQuery.value;
			if (search != null) {
				normalizedSearch = search.trim().toLowerCase();
				if (search !== normalizedSearch) {
					search = normalizedSearch;
					fixLocationQuery(i18n.search || 'search', search || null);
				}
			}
			if (search) {
				users = users.filter(getFilter(search));
				snapshotData.search = search;
			}
		}
		snapshot = db.User.dataSnapshots.get(serializeSnapshotKey(snapshotData));

		pagination.count.value = isPartial.value
			? snapshot._totalSize.map(getPageCount) : users._size.map(getPageCount);
		maxPage = pagination.count.value;

		// Resolve page
		if (pageQuery.value != null) {
			page = toNatural(pageQuery.value) || null;
			if (page === 1) page = null;
			if (page) {
				if (maxPage < page) page = (maxPage > 1) ? maxPage : null;
			}
			if (page) page = String(page);
			if (page !== pageQuery.value) fixLocationQuery(i18n.page || 'page', page);
			if (page) page = Number(page);
		}
		pagination.current.value = page || 1;

		// Update table
		if (isPartial.value) {
			// We rely on snapshots functionality
			snapshotData.page = page || 1;
			// Inform remote
			snapshotKey = serializeSnapshotKey(snapshotData);
			if (snapshots.last !== snapshotKey) snapshots.add(snapshotKey);
			if (page) {
				// Rely on remote prepared snapshot
				usersSnapshot = getUsersSnapshot(snapshot._get(page));
				inSync.value = usersSnapshot._size.gt(0);
				list.set = usersSnapshot;
				list.page = 1;
				return;
			}
			inSync.value = snapshot._totalSize.map(function (value) {
				if (value == null) return false;
				return users._size.gtOrEq(value > pageLimit ? pageLimit : value);
			});
		} else {
			// Assure that we have all data on board
			snapshotData = { appName: appName };
			if (statusMap[i18n.all || 'all']) snapshotData.status = i18n.all || 'all';
			snapshotKey = serializeSnapshotKey(snapshotData);
			if (snapshots.last !== snapshotKey) snapshots.add(snapshotKey);
		}
		list.set = users;
		list.page = page || 1;
		inSync.value = true;
	});

	// Setup
	// Status filter
	if (keys(statusMap).length > 1) {
		statusQuery = location.query.get(i18n.status || 'status');
		statusQuery.on('change', update);
	}

	// Search filter
	if (options.searchFilter) {
		searchQuery = location.query.get(i18n.search || 'search');
		searchQuery.on('change', update);
	}

	// Pagination
	pageQuery = location.query.get(i18n.page || 'page');
	pageQuery.on('change', update);

	// Table configuration
	list = new ReactiveList(statusMap[''], options.compare);
	list.limit = pageLimit;
	table = new ReactiveTable(document, list, columns);
	table.pagination = pagination = new Pagination(pathname);

	table.inSync = inSync;
	tableStyle = window.getComputedStyle(table.table);
	inSync.on('change', function (event) {
		table.table.style.height = event.newValue ? '' : tableStyle.height;
		table.table.classList[event.newValue ? 'remove' : 'add']('not-in-sync');
	});

	if (options.id) table.table.id = options.id;
	if (options.class) table.table.className = options.class;

	location.on('change', function () {
		active = (this.pathname === pathname);
		update();
	});
	active = location.pathname === pathname;

	isPartial.on('change', update);
	window.addEventListener('focus', update, false);
	snapshots.on('change', function () {
		if (document.hasFocus && !document.hasFocus()) return;
		if (!snapshots.has(snapshotKey)) snapshots.add(snapshotKey);
	});

	update();
	return table;
};
<|endoftext|>


<commit_before>var request = require('request');
var cheerio = require('cheerio');
var h = require('../../lib/helpers.js');

exports.setup = function ( server ) {
	server.get({path: '/cinema', version: '1.0.0'}, getMovies);
};

/**
 * Fetches movies for show today in Icelandic cinemas.
 * response - JSON: Movie data within an 'results' array.
 */
var getMovies = function (req, res, next) {
	res.charSet = 'utf8';

	var url = 'http://kvikmyndir.is/bio/syningatimar/';

	request(url, function (error, response, body) {
		if (error) return exports.logError( error );

		// Cheerio declared and then attemted to load.
		var $;

		try {
			$ = cheerio.load( body );
		} catch (e) {
			throw new Error( e );
		}

		// Base object to be added to
		// and eventually sent as a JSON response.
		var obj = {
			results: []
		};

		// DOM elements array containing all movies.
		var movies = $('.Kvikmyndir_TimeTable #divbox').find('.utanumMynd_new');

		// Loop through movies
		movies.each(function() {
			// This movie.
			var movie = $(this);

			// Showtimes for JSON
			var showtimes = [];

			// Find all theaters and loop through them.
			var theaters = movie.find('[id^="myndbio"]');
			theaters.each(function() {
				// Single theater
				var theater = {
					theater: $(this).find('#bio a').text().trim(),
					schedule: []
				};

				// Loop through each showtime and
				// add them to the theater schedule.
				$(this).find('.syningartimi_item').each(function() {
					theater.schedule.push( $(this).text().trim() );
				});

				// Add theater to showtimes array.
				showtimes.push( theater );
			});

			// Create an object of info
			// and add it to the 'results' array.
			obj.results.push({
				title: movie.find('#mynd_titill a').html().trim(),
				released: movie.find('.mynd_titill_artal').text().match(/\d+/g)[0].trim(),
				restricted: movie.find('.mynd_aldurstakmark img').attr('alt').trim(),
				imdb: movie.find('.imdbEinkunn').text().trim(),
				image: 'http://kvikmyndir.is' + movie.find('img.poster').attr('src').trim(),
				showtimes: showtimes
			});
		});

		res.json(200, obj);
		return next();
	});
};<commit_msg>Throws a new Error where it should.<commit_after>var request = require('request');
var cheerio = require('cheerio');
var h = require('../../lib/helpers.js');

exports.setup = function ( server ) {
	server.get({path: '/cinema', version: '1.0.0'}, getMovies);
};

/**
 * Fetches movies for show today in Icelandic cinemas.
 * response - JSON: Movie data within an 'results' array.
 */
var getMovies = function (req, res, next) {
	res.charSet = 'utf8';

	var url = 'http://kvikmyndir.is/bio/syningatimar/';

	request(url, function (error, response, body) {
		if (error) throw new Error( url + ' not responding correctly...' );

		// Cheerio declared and then attemted to load.
		var $;

		try {
			$ = cheerio.load( body );
		} catch (e) {
			throw new Error( 'Could not load the body with cherrio.' );
		}

		// Base object to be added to
		// and eventually sent as a JSON response.
		var obj = {
			results: []
		};

		// DOM elements array containing all movies.
		var movies = $('.Kvikmyndir_TimeTable #divbox').find('.utanumMynd_new');

		// Loop through movies
		movies.each(function() {
			// This movie.
			var movie = $(this);

			// Showtimes for JSON
			var showtimes = [];

			// Find all theaters and loop through them.
			var theaters = movie.find('[id^="myndbio"]');
			theaters.each(function() {
				// Single theater
				var theater = {
					theater: $(this).find('#bio a').text().trim(),
					schedule: []
				};

				// Loop through each showtime and
				// add them to the theater schedule.
				$(this).find('.syningartimi_item').each(function() {
					theater.schedule.push( $(this).text().trim() );
				});

				// Add theater to showtimes array.
				showtimes.push( theater );
			});

			// Create an object of info
			// and add it to the 'results' array.
			obj.results.push({
				title: movie.find('#mynd_titill a').html().trim(),
				released: movie.find('.mynd_titill_artal').text().match(/\d+/g)[0].trim(),
				restricted: movie.find('.mynd_aldurstakmark img').attr('alt').trim(),
				imdb: movie.find('.imdbEinkunn').text().trim(),
				image: 'http://kvikmyndir.is' + movie.find('img.poster').attr('src').trim(),
				showtimes: showtimes
			});
		});

		res.json(200, obj);
		return next();
	});
};<|endoftext|>


<commit_before>'use strict';

exports.get = function get(object, keypath) {
  return object.getIn(keypath);
};

exports.set = function set(object, keypath, value) {
  return object.setIn(object, keypath, value);
};

exports.equals = function equals(a, b) {
  return a === b || a.equals(b);
};
<commit_msg>Expose a ready-made dispatcher for Immutable.js<commit_after>'use strict';

var Dispatcher = require('.');

function get(object, keypath) {
  return object.getIn(keypath);
}

function set(object, keypath, value) {
  return object.setIn(object, keypath, value);
}

function equals(a, b) {
  return a === b || a.equals(b);
}

exports = module.exports = function DispatcherForImmutable() {
  return Dispatcher(get, set, equals);
};

exports.get = get;
exports.set = set;
exports.equals = equals;
<|endoftext|>


<commit_before>/* global _ */
'use strict';

angular.module('metadatamanagementApp')
  .factory('RelatedPublicationSearchService',
    function(ElasticSearchClient, $q, CleanJSObjectService,
      SearchHelperService) {
      var createQueryObject = function(type) {
        type = type || 'related_publications';
        return {
          index: type,
          type: type
        };
      };

      var createTermFilters = function(filter, dataAcquisitionProjectId, type) {
        type = type || 'related_publications';
        var termFilter;
        if (!CleanJSObjectService.isNullOrEmpty(filter) ||
          !CleanJSObjectService.isNullOrEmpty(dataAcquisitionProjectId)) {
          termFilter = [];
        }
        if (!CleanJSObjectService.isNullOrEmpty(dataAcquisitionProjectId)) {
          var projectFilter = {
            term: {
              dataAcquisitionProjectId: dataAcquisitionProjectId
            }
          };
          termFilter.push(projectFilter);
        }
        if (!CleanJSObjectService.isNullOrEmpty(filter)) {
          termFilter = _.concat(termFilter,
            SearchHelperService.createTermFilters(type, filter));
        }
        return termFilter;
      };

      var findOneById = function(id, attributes) {
        var deferred = $q.defer();
        var query = createQueryObject();
        query.id = id;
        query._source = attributes;
        ElasticSearchClient.getSource(query, function(error, response) {
            if (error) {
              deferred.reject(error);
            } else {
              deferred.resolve(response);
            }
          });
        return deferred;
      };
      var findBySurveyId = function(surveyId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'surveyIds': surveyId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };
      var findByQuestionId = function(questionId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'questionIds': questionId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };

      var findByInstrumentId = function(instrumentId, selectedAttributes,
        from, size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'instrumentIds': instrumentId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };
      var findByDataSetId = function(dataSetId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'dataSetIds': dataSetId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };
      var findByStudyId = function(studyId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'studyIds': studyId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };

      var findTitles = function(searchText, filter, type,
        queryterm, dataAcquisitionProjectId) {
        var query = createQueryObject(type);
        query.size = 0;
        query.body = {};
        var termFilters = createTermFilters(filter, dataAcquisitionProjectId,
          type);
        var prefix = (type === 'related_publications' || !type)  ? ''
          : 'nestedRelatedPublications.';
        var aggregation = {
            'aggs': {
              'title': {
                'filter': {
                  'bool': {
                    'must': [{
                      'match': {

                      }
                    }]
                  }
                },
                'aggs': {
                  'id': {
                    'terms': {
                      'field': prefix + 'id',
                      'size': 100
                    },
                    'aggs': {
                      'title': {
                        'terms': {
                          'field': prefix + 'title',
                          'size': 100
                        },
                        'aggs': {
                          'language': {
                            'terms': {
                              'field': prefix + 'language',
                              'size': 100
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          };
        var nestedAggregation = {
          'aggs': {
              'relatedPublications': {
                'nested': {
                  'path': 'nestedRelatedPublications'
                }
              }
            }
        };

        aggregation.aggs.title.filter.bool.must[0]
        .match[prefix + 'completeTitle.de'] =  {
          'query': searchText,
          'operator': 'AND',
          'minimum_should_match': '100%',
          'zero_terms_query': 'ALL'
        };

        if (prefix !== '') {
          nestedAggregation.aggs.relatedPublications.aggs =
            aggregation.aggs;
          query.body.aggs = nestedAggregation.aggs;
        } else {
          query.body.aggs = aggregation.aggs;
        }

        if (termFilters) {
          query.body.query = query.body.query || {};
          query.body.query.bool = query.body.query.bool || {};
          query.body.query.bool.filter = termFilters;
        }

        SearchHelperService.addQuery(query, queryterm);

        SearchHelperService.addFilter(query);

        return ElasticSearchClient.search(query).then(function(result) {
          var titles = [];
          var titleElement = {};
          var buckets = [];
          if (prefix !== '') {
            buckets = result.aggregations.relatedPublications.title.id.buckets;
          } else {
            buckets = result.aggregations.title.id.buckets;
          }
          buckets.forEach(function(bucket) {
              titleElement = {
                title: bucket.title.buckets[0].key,
                language: bucket.title.buckets[0].language.buckets[0].key,
                id: bucket.key,
                count: bucket.doc_count
              };
              titles.push(titleElement);
            });
          return titles;
        });
      };

      return {
        findOneById: findOneById,
        findBySurveyId: findBySurveyId,
        findByDataSetId: findByDataSetId,
        findByQuestionId: findByQuestionId,
        findByStudyId: findByStudyId,
        findByInstrumentId: findByInstrumentId,
        findTitles: findTitles
      };
    });
<commit_msg>#2211 fix filter search query<commit_after>/* global _ */
'use strict';

angular.module('metadatamanagementApp')
  .factory('RelatedPublicationSearchService',
    function(ElasticSearchClient, $q, CleanJSObjectService,
      SearchHelperService) {
      var createQueryObject = function(type) {
        type = type || 'related_publications';
        return {
          index: type,
          type: type
        };
      };

      var createTermFilters = function(filter, dataAcquisitionProjectId, type) {
        type = type || 'related_publications';
        var termFilter;
        if (!CleanJSObjectService.isNullOrEmpty(filter) ||
          !CleanJSObjectService.isNullOrEmpty(dataAcquisitionProjectId)) {
          termFilter = [];
        }
        if (!CleanJSObjectService.isNullOrEmpty(dataAcquisitionProjectId)) {
          var projectFilter = {
            term: {
              dataAcquisitionProjectId: dataAcquisitionProjectId
            }
          };
          termFilter.push(projectFilter);
        }
        if (!CleanJSObjectService.isNullOrEmpty(filter)) {
          termFilter = _.concat(termFilter,
            SearchHelperService.createTermFilters(type, filter));
        }
        return termFilter;
      };

      var findOneById = function(id, attributes) {
        var deferred = $q.defer();
        var query = createQueryObject();
        query.id = id;
        query._source = attributes;
        ElasticSearchClient.getSource(query, function(error, response) {
            if (error) {
              deferred.reject(error);
            } else {
              deferred.resolve(response);
            }
          });
        return deferred;
      };
      var findBySurveyId = function(surveyId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'surveyIds': surveyId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };
      var findByQuestionId = function(questionId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'questionIds': questionId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };

      var findByInstrumentId = function(instrumentId, selectedAttributes,
        from, size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'instrumentIds': instrumentId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };
      var findByDataSetId = function(dataSetId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'dataSetIds': dataSetId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };
      var findByStudyId = function(studyId, selectedAttributes, from,
        size) {
        var query = createQueryObject();
        query.body = {};
        query.body.from = from;
        query.body.size = size;
        query.body._source = selectedAttributes;
        query.body.query = {
          'bool': {
            'must': [{
              'match_all': {}
            }],
            'filter': [{
              'term': {
                'studyIds': studyId
              }
            }]
          }
        };
        return ElasticSearchClient.search(query);
      };

      var findTitles = function(searchText, filter, type,
        queryterm, dataAcquisitionProjectId) {
        var query = createQueryObject(type);
        query.size = 0;
        query.body = {};
        var termFilters = createTermFilters(filter, dataAcquisitionProjectId,
          type);
        var prefix = (type === 'related_publications' || !type)  ? ''
          : 'nestedRelatedPublications.';
        var aggregation = {
            'aggs': {
              'title': {
                'filter': {
                  'bool': {
                    'must': [{
                      'match': {

                      }
                    }]
                  }
                },
                'aggs': {
                  'id': {
                    'terms': {
                      'field': prefix + 'id',
                      'size': 100
                    },
                    'aggs': {
                      'title': {
                        'terms': {
                          'field': prefix + 'title',
                          'size': 100
                        },
                        'aggs': {
                          'language': {
                            'terms': {
                              'field': prefix + 'language',
                              'size': 100
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          };
        var nestedAggregation = {
          'aggs': {
              'relatedPublications': {
                'nested': {
                  'path': 'nestedRelatedPublications'
                }
              }
            }
        };

        aggregation.aggs.title.filter.bool.must[0]
        .match[prefix + 'completeTitle.de'] =  {
          'query': searchText,
          'operator': 'AND',
          'minimum_should_match': '100%',
          'zero_terms_query': 'ALL'
        };

        if (prefix !== '') {
          nestedAggregation.aggs.relatedPublications.aggs =
            aggregation.aggs;
          query.body.aggs = nestedAggregation.aggs;
        } else {
          query.body.aggs = aggregation.aggs;
        }

        if (termFilters) {
          query.body.query = query.body.query || {};
          query.body.query.bool = query.body.query.bool || {};
          query.body.query.bool.filter = termFilters;
        }

        SearchHelperService.addQuery(query, queryterm);

        SearchHelperService.addFilter(query);
        SearchHelperService.addShadowCopyFilter(query, filter);

        return ElasticSearchClient.search(query).then(function(result) {
          var titles = [];
          var titleElement = {};
          var buckets = [];
          if (prefix !== '') {
            buckets = result.aggregations.relatedPublications.title.id.buckets;
          } else {
            buckets = result.aggregations.title.id.buckets;
          }
          buckets.forEach(function(bucket) {
              titleElement = {
                title: bucket.title.buckets[0].key,
                language: bucket.title.buckets[0].language.buckets[0].key,
                id: bucket.key,
                count: bucket.doc_count
              };
              titles.push(titleElement);
            });
          return titles;
        });
      };

      return {
        findOneById: findOneById,
        findBySurveyId: findBySurveyId,
        findByDataSetId: findByDataSetId,
        findByQuestionId: findByQuestionId,
        findByStudyId: findByStudyId,
        findByInstrumentId: findByInstrumentId,
        findTitles: findTitles
      };
    });
<|endoftext|>