<commit_before>import OrchestraRouter     from '../OrchestraRouter'
import Application         from '../../Application'
import FormBuilder         from '../../../Service/Form/Model/FormBuilder'
import ContentSummaryView  from '../../View/Content/ContentSummaryView'
import ContentsView        from '../../View/Content/ContentsView'
import ContentFormView     from '../../View/Content/ContentFormView'
import NewContentFormView  from '../../View/Content/NewContentFormView'
import ContentTypes        from '../../Collection/ContentType/ContentTypes'
import Contents            from '../../Collection/Content/Contents'
import ContentType         from '../../Model/ContentType/ContentType'
import Content             from '../../Model/Content/Content'
import Statuses            from '../../Collection/Status/Statuses'
import ApplicationError    from '../../../Service/Error/ApplicationError'
import ConfirmModalView    from '../../../Service/ConfirmModal/View/ConfirmModalView'
import ContentVersionsView from '../../View/Content/ContentVersionsView'

/**
 * @class ContentRouter
 */
class ContentRouter extends OrchestraRouter
{
    /**
     * @inheritdoc
     */
    preinitialize() {
        let summaryRoute = 'content/summary';

        this.routes = {
            summaryRoute                                                         : 'showContentSummary',
            'content/list/:contentTypeId/:language(/:page)'                      : 'listContent',
            'content/edit/:contentTypeId/:language/:contentId(/:version)'        : 'editContent',
            'content/new/:contentTypeId/:language'                               : 'newContent',
            'content/manage-versions/:contentTypeId/:language/:contentId(/:page)': 'manageVersionsContent'
        };

        this._cachedContentTypes = {};

        this._breadcrumbItems = [
            {
                label: Translator.trans('open_orchestra_backoffice.menu.contribution.title')
            },
            {
                label: Translator.trans('open_orchestra_backoffice.menu.contribution.content'),
                link: '#' + summaryRoute
            }
        ];
    }

    /**
     * Get a cached content type
     *
     * @param {string} contentTypeId
     * @returns {Object}
     * private
     */
    _getCachedContentType(contentTypeId) {
        return this._cachedContentTypes[contentTypeId];
    }

    /**
     * Cache a content type
     *
     * @param {Object} contentType
     */
    _cacheContentType(contentType) {
        this._cachedContentTypes[contentType.get('content_type_id')] = contentType;
    }

    /**
     * @inheritdoc
     */
    getBreadcrumb(args, name) {
        let contentTypesRoute = ['listContent', 'newContent', 'editContent', 'manageVersionsContent'];

        if (contentTypesRoute.indexOf(name) > -1) {
            return this._getBreadcrumbWithContentType(args[0]);
        }

        return this._breadcrumbItems;
    }

    /**
     * Get breadcrumb with Content Type
     * @param {string} contentTypeId
     * @returns {Object}
     */
    _getBreadcrumbWithContentType(contentTypeId) {
        let contentType = this._getCachedContentType(contentTypeId);
        if (contentType instanceof ContentType) {
            return this._breadcrumbItems.concat([{label: contentType.get('name')}]);
        }

        let deferred = jQuery.Deferred();
        contentType = new ContentType();

        contentType.fetch({
            urlParameter: {contentTypeId: contentTypeId},
            success: () => {
                this._cacheContentType(contentType);
                deferred.resolve(this._breadcrumbItems.concat([{label: contentType.get('name')}]));
            }
        });

        return deferred.promise();
    }

    /**
     * @inheritdoc
     */
    getMenuHighlight() {
        return {
            '*' : 'navigation-content'
        };
    }

    /**
     * Manage version content
     *
     * @param {string} contentTypeId
     * @param {string} language
     * @param {string} contentId
     * @param {string} page
     */
    manageVersionsContent(contentTypeId, language, contentId, page) {
        this._displayLoader(Application.getRegion('content'));

        if (null === page) {
            page = 1
        }
        page = Number(page) - 1;

        let contentVersions = new Contents();

        contentVersions.fetch({
            apiContext: 'list-version',
            urlParameter: {
                language: language,
                contentId: contentId
            },
            success: () => {
                let contentVersionsView = new ContentVersionsView({
                    collection: contentVersions,
                    settings: {
                        page: page,
                        pageLength: Application.getConfiguration().getParameter('datatable').pageLength
                    },
                    contentId: contentId,
                    language: language,
                    contentType: this._getCachedContentType(contentTypeId),
                    siteLanguages: Application.getContext().siteLanguages
                });

                Application.getRegion('content').html(contentVersionsView.render().$el);
            }
        });
    }

    /**
     * show content summary
     */
    showContentSummary() {
        this._displayLoader(Application.getRegion('content'));
        let contentTypes = new ContentTypes();

        contentTypes.fetch({
            apiContext: 'list_content_type_for_content',
            success: () => {
                let contentSummaryView = new ContentSummaryView({
                    contentTypes: contentTypes
                });
                let el = contentSummaryView.render().$el;
                Application.getRegion('content').html(el);
            }
        });
    }

    /**
     * Edit content
     *
     * @param {string}   contentTypeId
     * @param {string}   language
     * @param {string}   contentId
     * @param {int|null} version
     */
    editContent(contentTypeId, language, contentId, version = null) {
        this._displayLoader(Application.getRegion('content'));

        let content = new Content({id: contentId});

        content.fetch({
            urlParameter: {version: version, language: language},
            enabledCallbackError: false,
            success: () => {
                let url = Routing.generate('open_orchestra_backoffice_content_form', {
                    contentId: contentId,
                    language : language,
                    version  : version
                });
                FormBuilder.createFormFromUrl(url, (form) => {
                    let contentFormView = new ContentFormView({
                        form         : form,
                        contentType  : this._getCachedContentType(contentTypeId),
                        content      : content,
                        siteLanguages: Application.getContext().siteLanguages
                    });
                    Application.getRegion('content').html(contentFormView.render().$el);
                });
            },
            error: () => {
                this._errorCallbackEdit(contentTypeId, contentId, language);
            }
        })
    }

    /**
     * Create contentType
     *
     * @param {string} contentTypeId
     * @param {string} language
     */
    newContent(contentTypeId, language) {
        this._displayLoader(Application.getRegion('content'));

        let url = Routing.generate('open_orchestra_backoffice_content_new', {
            contentTypeId: contentTypeId,
            language     : language
        });

        FormBuilder.createFormFromUrl(url, (form) => {
            let newContentFormView = new NewContentFormView({
                form           : form,
                contentTypeId  : contentTypeId,
                contentTypeName: this._getCachedContentType(contentTypeId).get('name'),
                language       : language,
                siteLanguages  : Application.getContext().siteLanguages
            });
            Application.getRegion('content').html(newContentFormView.render().$el);
        });
    }

    /**
     * list content by content type
     */
    listContent(contentTypeId, language, page) {
        if (null === page) {
            page = 1
        }
        this._displayLoader(Application.getRegion('content'));
        let pageLength = Application.getConfiguration().getParameter('datatable').pageLength;
        page = Number(page) - 1;
        let urlParameter = {
            contentTypeId: contentTypeId,
            siteId: Application.getContext().siteId,
            language: language
        };

        let contentType = new ContentType();
        let statuses = new Statuses();
        let contents = new Contents();

        $.when(
            statuses.fetch({apiContext: 'contents'}),
            contentType.fetch({urlParameter: {contentTypeId: contentTypeId}}),
            contents.fetch({
                apiContext: 'list',
                urlParameter: urlParameter,
                data : {
                    start: page * pageLength,
                    length: pageLength
                }
            })
        ).done( () => {
            let contentsView = new ContentsView({
                collection: contents,
                settings: {
                    page: page,
                    deferLoading: [contents.recordsTotal, contents.recordsFiltered],
                    data: contents.models,
                    pageLength: pageLength
                },
                urlParameter: urlParameter,
                contentType: contentType,
                statuses: statuses
            });
            let el = contentsView.render().$el;
            Application.getRegion('content').html(el);
         });
    }

    /**
     * Callback if content not existing in specific language
     * Show popin to create content in this language
     *
     * @private
     */
    _errorCallbackEdit(contentTypeId, contentId, language) {
        let noCallback = () => {
            let url = Backbone.history.generateUrl('listContent',{
                contentTypeId: contentTypeId,
                language: language
            });
            Backbone.history.navigate(url, true);
        };
        let yesCallback = () => {
            new Content().save({}, {
                apiContext: 'new-language',
                urlParameter: {
                    contentId: contentId,
                    language: language
                },
                success: () => {
                    Backbone.history.loadUrl(Backbone.history.fragment);
                }
            })
        };

        let confirmModalView = new ConfirmModalView({
            confirmTitle: Translator.trans('open_orchestra_backoffice.content.confirm_create.title'),
            confirmMessage: Translator.trans('open_orchestra_backoffice.content.confirm_create.message'),
            context: this,
            yesCallback: yesCallback,
            noCallback: noCallback
        });

        Application.getRegion('modal').html(confirmModalView.render().$el);
        confirmModalView.show();
    }
}

export default ContentRouter;
<commit_msg>Fix broken content summary route (#2315)<commit_after>import OrchestraRouter     from '../OrchestraRouter'
import Application         from '../../Application'
import FormBuilder         from '../../../Service/Form/Model/FormBuilder'
import ContentSummaryView  from '../../View/Content/ContentSummaryView'
import ContentsView        from '../../View/Content/ContentsView'
import ContentFormView     from '../../View/Content/ContentFormView'
import NewContentFormView  from '../../View/Content/NewContentFormView'
import ContentTypes        from '../../Collection/ContentType/ContentTypes'
import Contents            from '../../Collection/Content/Contents'
import ContentType         from '../../Model/ContentType/ContentType'
import Content             from '../../Model/Content/Content'
import Statuses            from '../../Collection/Status/Statuses'
import ApplicationError    from '../../../Service/Error/ApplicationError'
import ConfirmModalView    from '../../../Service/ConfirmModal/View/ConfirmModalView'
import ContentVersionsView from '../../View/Content/ContentVersionsView'

/**
 * @class ContentRouter
 */
class ContentRouter extends OrchestraRouter
{
    /**
     * @inheritdoc
     */
    preinitialize() {
        this.routes = {
            'content/summary'                                                    : 'showContentSummary',
            'content/list/:contentTypeId/:language(/:page)'                      : 'listContent',
            'content/edit/:contentTypeId/:language/:contentId(/:version)'        : 'editContent',
            'content/new/:contentTypeId/:language'                               : 'newContent',
            'content/manage-versions/:contentTypeId/:language/:contentId(/:page)': 'manageVersionsContent'
        };

        this._cachedContentTypes = {};

        this._breadcrumbItems = [
            {
                label: Translator.trans('open_orchestra_backoffice.menu.contribution.title')
            },
            {
                label: Translator.trans('open_orchestra_backoffice.menu.contribution.content'),
                link: '#content/summary'
            }
        ];
    }

    /**
     * Get a cached content type
     *
     * @param {string} contentTypeId
     * @returns {Object}
     * private
     */
    _getCachedContentType(contentTypeId) {
        return this._cachedContentTypes[contentTypeId];
    }

    /**
     * Cache a content type
     *
     * @param {Object} contentType
     */
    _cacheContentType(contentType) {
        this._cachedContentTypes[contentType.get('content_type_id')] = contentType;
    }

    /**
     * @inheritdoc
     */
    getBreadcrumb(args, name) {
        let contentTypesRoute = ['listContent', 'newContent', 'editContent', 'manageVersionsContent'];

        if (contentTypesRoute.indexOf(name) > -1) {
            return this._getBreadcrumbWithContentType(args[0]);
        }

        return this._breadcrumbItems;
    }

    /**
     * Get breadcrumb with Content Type
     * @param {string} contentTypeId
     * @returns {Object}
     */
    _getBreadcrumbWithContentType(contentTypeId) {
        let contentType = this._getCachedContentType(contentTypeId);
        if (contentType instanceof ContentType) {
            return this._breadcrumbItems.concat([{label: contentType.get('name')}]);
        }

        let deferred = jQuery.Deferred();
        contentType = new ContentType();

        contentType.fetch({
            urlParameter: {contentTypeId: contentTypeId},
            success: () => {
                this._cacheContentType(contentType);
                deferred.resolve(this._breadcrumbItems.concat([{label: contentType.get('name')}]));
            }
        });

        return deferred.promise();
    }

    /**
     * @inheritdoc
     */
    getMenuHighlight() {
        return {
            '*' : 'navigation-content'
        };
    }

    /**
     * Manage version content
     *
     * @param {string} contentTypeId
     * @param {string} language
     * @param {string} contentId
     * @param {string} page
     */
    manageVersionsContent(contentTypeId, language, contentId, page) {
        this._displayLoader(Application.getRegion('content'));

        if (null === page) {
            page = 1
        }
        page = Number(page) - 1;

        let contentVersions = new Contents();

        contentVersions.fetch({
            apiContext: 'list-version',
            urlParameter: {
                language: language,
                contentId: contentId
            },
            success: () => {
                let contentVersionsView = new ContentVersionsView({
                    collection: contentVersions,
                    settings: {
                        page: page,
                        pageLength: Application.getConfiguration().getParameter('datatable').pageLength
                    },
                    contentId: contentId,
                    language: language,
                    contentType: this._getCachedContentType(contentTypeId),
                    siteLanguages: Application.getContext().siteLanguages
                });

                Application.getRegion('content').html(contentVersionsView.render().$el);
            }
        });
    }

    /**
     * show content summary
     */
    showContentSummary() {
        this._displayLoader(Application.getRegion('content'));
        let contentTypes = new ContentTypes();

        contentTypes.fetch({
            apiContext: 'list_content_type_for_content',
            success: () => {
                let contentSummaryView = new ContentSummaryView({
                    contentTypes: contentTypes
                });
                let el = contentSummaryView.render().$el;
                Application.getRegion('content').html(el);
            }
        });
    }

    /**
     * Edit content
     *
     * @param {string}   contentTypeId
     * @param {string}   language
     * @param {string}   contentId
     * @param {int|null} version
     */
    editContent(contentTypeId, language, contentId, version = null) {
        this._displayLoader(Application.getRegion('content'));

        let content = new Content({id: contentId});

        content.fetch({
            urlParameter: {version: version, language: language},
            enabledCallbackError: false,
            success: () => {
                let url = Routing.generate('open_orchestra_backoffice_content_form', {
                    contentId: contentId,
                    language : language,
                    version  : version
                });
                FormBuilder.createFormFromUrl(url, (form) => {
                    let contentFormView = new ContentFormView({
                        form         : form,
                        contentType  : this._getCachedContentType(contentTypeId),
                        content      : content,
                        siteLanguages: Application.getContext().siteLanguages
                    });
                    Application.getRegion('content').html(contentFormView.render().$el);
                });
            },
            error: () => {
                this._errorCallbackEdit(contentTypeId, contentId, language);
            }
        })
    }

    /**
     * Create contentType
     *
     * @param {string} contentTypeId
     * @param {string} language
     */
    newContent(contentTypeId, language) {
        this._displayLoader(Application.getRegion('content'));

        let url = Routing.generate('open_orchestra_backoffice_content_new', {
            contentTypeId: contentTypeId,
            language     : language
        });

        FormBuilder.createFormFromUrl(url, (form) => {
            let newContentFormView = new NewContentFormView({
                form           : form,
                contentTypeId  : contentTypeId,
                contentTypeName: this._getCachedContentType(contentTypeId).get('name'),
                language       : language,
                siteLanguages  : Application.getContext().siteLanguages
            });
            Application.getRegion('content').html(newContentFormView.render().$el);
        });
    }

    /**
     * list content by content type
     */
    listContent(contentTypeId, language, page) {
        if (null === page) {
            page = 1
        }
        this._displayLoader(Application.getRegion('content'));
        let pageLength = Application.getConfiguration().getParameter('datatable').pageLength;
        page = Number(page) - 1;
        let urlParameter = {
            contentTypeId: contentTypeId,
            siteId: Application.getContext().siteId,
            language: language
        };

        let contentType = new ContentType();
        let statuses = new Statuses();
        let contents = new Contents();

        $.when(
            statuses.fetch({apiContext: 'contents'}),
            contentType.fetch({urlParameter: {contentTypeId: contentTypeId}}),
            contents.fetch({
                apiContext: 'list',
                urlParameter: urlParameter,
                data : {
                    start: page * pageLength,
                    length: pageLength
                }
            })
        ).done( () => {
            let contentsView = new ContentsView({
                collection: contents,
                settings: {
                    page: page,
                    deferLoading: [contents.recordsTotal, contents.recordsFiltered],
                    data: contents.models,
                    pageLength: pageLength
                },
                urlParameter: urlParameter,
                contentType: contentType,
                statuses: statuses
            });
            let el = contentsView.render().$el;
            Application.getRegion('content').html(el);
         });
    }

    /**
     * Callback if content not existing in specific language
     * Show popin to create content in this language
     *
     * @private
     */
    _errorCallbackEdit(contentTypeId, contentId, language) {
        let noCallback = () => {
            let url = Backbone.history.generateUrl('listContent',{
                contentTypeId: contentTypeId,
                language: language
            });
            Backbone.history.navigate(url, true);
        };
        let yesCallback = () => {
            new Content().save({}, {
                apiContext: 'new-language',
                urlParameter: {
                    contentId: contentId,
                    language: language
                },
                success: () => {
                    Backbone.history.loadUrl(Backbone.history.fragment);
                }
            })
        };

        let confirmModalView = new ConfirmModalView({
            confirmTitle: Translator.trans('open_orchestra_backoffice.content.confirm_create.title'),
            confirmMessage: Translator.trans('open_orchestra_backoffice.content.confirm_create.message'),
            context: this,
            yesCallback: yesCallback,
            noCallback: noCallback
        });

        Application.getRegion('modal').html(confirmModalView.render().$el);
        confirmModalView.show();
    }
}

export default ContentRouter;
<|endoftext|>


<commit_before>import React from 'react';
import _ from 'lodash';
//
import * as Basic from '../../basic';
import AbstractFormAttributeRenderer from './AbstractFormAttributeRenderer';
import UuidFormAttributeRenderer from './UuidFormAttributeRenderer';
import { IdentityManager } from '../../../redux/';

const manager = new IdentityManager();

/**
 * Uuid form value component
 * - supports multiple attributes
 * - TODO: validation
 *
 * @author Radek Tomiška
 */
export default class IdentitySelectFormAttributeRenderer extends UuidFormAttributeRenderer {

  /**
   * Returns true, when multi value mode is supported
   *
   * @return {boolean}
   */
  supportsMultiple() {
    return true;
  }

  /**
   * Fill form value field by persistent type from input value
   *
   * @param  {FormValue} formValue - form value
   * @param  {object} formComponent value
   * @return {FormValue}
   */
  fillFormValue(formValue, rawValue) {
    formValue.uuidValue = rawValue;
    // TODO: validations for uuid
    return formValue;
  }

  /**
   * Returns value to ipnut from given (persisted) form value
   *
   * @param  {FormValue} formValue
   * @return {object} value by persistent type
   */
  getInputValue(formValue) {
    return formValue.uuidValue ? formValue.uuidValue : formValue.value;
  }

  /**
   * Form values from multi input
   *
   * @return {arrayOf(FormValue)}
   */
  toFormValues() {
    const { values } = this.props;
    const formComponent = this.refs[AbstractFormAttributeRenderer.INPUT];
    const filledFormValues = [];
    //
    if (!formComponent) {
      // not supported compoenents
      return filledFormValues;
    }
    const componentValues = formComponent.getValue();
    // undefined values are not sent (confidential properties  etc.)
    if (componentValues === undefined) {
      return filledFormValues;
    }
    if (componentValues) {
      for (let i = 0; i < componentValues.length; i++) {
        let formValue = null;
        if (values && i < values.length) {
          formValue = values[i];
        }
        filledFormValues.push(this.fillFormValue(this.prepareFormValue(formValue, i), componentValues[i]));
      }
    }
    return filledFormValues;
  }

  toInputValues(formValues) {
    const { attribute } = this.props;
    //
    let formValue = null;
    if (formValues && _.isArray(formValues)) {
      if (formValues.length > 0) {
        const results = [];
        formValues.forEach(singleValue => {
          results.push(this.getInputValue(singleValue));
        });
        return results;
      }
    } else {
      formValue = formValues;
    }
    if (formValue === null) {
      return attribute.defaultValue;
    }
    return this.getInputValue(formValue);
  }

  renderSingleInput() {
    const { attribute, readOnly, values } = this.props;
    //
    return (
      <Basic.SelectBox
        ref={ AbstractFormAttributeRenderer.INPUT }
        label={ this.getLabel() }
        placeholder={ attribute.placeholder }
        manager={ manager }
        value={ !attribute.multiple ? this.toInputValue(values) : this.toInputValues(values) }
        helpBlock={ this.getHelpBlock() }
        readOnly={ readOnly || attribute.readonly }
        required={ attribute.required }
        multiSelect={ attribute.multiple }/>
    );
  }

  renderMultipleInput() {
    return this.renderSingleInput();
  }
}
<commit_msg>#1081 - Bulk operation on identities - review<commit_after><|endoftext|>


<commit_before>"use strict";
var binaryHelper = {
    blobToArrayBuffer: function blobToArrayBuffer(blob) {
        return new Promise(function (resolve, reject) {
            var reader = new FileReader();
            reader.addEventListener("loadend", function () {
                // reader.result contains the contents of blob as a typed array
                resolve(this.result);
            });
            reader.addEventListener("error", reject);
            reader.readAsArrayBuffer(blob);
        });
    },
    arrayBufferToBinary: function ArrayBufferToString(buffer) {
        var arr = new Uint8Array(buffer);
        return binaryHelper.fromCharCodes(arr, buffer.byteLength);
    },
    arrayBufferToString: function (buffer) {
        return binaryHelper.binaryToString(binaryHelper.arrayBufferToBinary(buffer));
    },
    /**
     * for nodejs
     * @param {ArrayBuffer} buffer
     * @returns {Buffer}
     */
    arrayToBuffer: function (buffer) {
        return new Buffer(new Uint8Array(buffer));
    },
    fromCharCodes: function (arr, len) {
        if (typeof(len) !== 'number') {
            len = arr.length;
        }
        var i = 0, str = "", fromCharCode = String.fromCharCode;
        for (; i < len; i++) {
            str += fromCharCode(arr[i]);
        }
        return str;
    },
    stringToArrayBuffer: function stringToArrayBuffer(string) {
        return binaryHelper.stringToUint8Array(string).buffer;
    },

    binaryToString: function binaryToString(binary) {
        var error;
        try {
            return decodeURIComponent(escape(binary));
        } catch (_error) {
            error = _error;
            if (error instanceof URIError) {
                return binary;
            } else {
                throw error;
            }
        }
    },

    stringToBinary: function stringToBinary(string) {
        var chars, code, i, isUCS2, len, _i;

        len = string.length;
        chars = [];
        isUCS2 = false;
        for (i = _i = 0; 0 <= len ? _i < len : _i > len; i = 0 <= len ? ++_i : --_i) {
            //code = String.prototype.charCodeAt.call(string, i);
            code = string.charCodeAt(i);
            if (code > 255) {
                isUCS2 = true;
                chars = null;
                break;
            } else {
                chars.push(code);
            }
        }
        if (isUCS2 === true) {
            return unescape(encodeURIComponent(string));
        } else {
            return chars && binaryHelper.fromCharCodes(chars);
        }
    },

    stringToUint8Array: function stringToUint8Array(string) {
        var binary, binLen, buffer, chars, i, _i;
        binary = binaryHelper.stringToBinary(string);
        binLen = binary.length;
        buffer = new ArrayBuffer(binLen);
        chars = new Uint8Array(buffer);
        for (i = _i = 0; 0 <= binLen ? _i < binLen : _i > binLen; i = 0 <= binLen ? ++_i : --_i) {
            //chars[i] = String.prototype.charCodeAt.call(binary, i);
            chars[i] = binary.charCodeAt(i);
        }
        return chars;
    },
    dataURLToBlob: function (dataURL) {
        var BASE64_MARKER = ';base64,', parts, contentType, raw, rawLength, uInt8Array;
        if (dataURL.indexOf(BASE64_MARKER) == -1) {
            parts = dataURL.split(',');
            contentType = parts[0].split(':')[1];
            raw = decodeURIComponent(parts[1]);

            return new Blob([raw], {type: contentType});
        }

        parts = dataURL.split(BASE64_MARKER);
        contentType = parts[0].split(':')[1];
        raw = window.atob(parts[1]);
        rawLength = raw.length;

        uInt8Array = new Uint8Array(rawLength);

        for (var i = 0; i < rawLength; ++i) {
            uInt8Array[i] = raw.charCodeAt(i);
        }

        return new Blob([uInt8Array], {type: contentType});
    },
    b64ImageToBlob: function b64ImageToBlob(b64, callback) {
        var img = new Image();

        img.onerror = callback;

        img.onload = function onload() {
            var canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;

            var ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

            canvas.toBlob(function(blob){
                callback && callback(null,blob);
            });
        };

        img.src = b64;
    }

};
export default binaryHelper;
<commit_msg>updated<commit_after>"use strict";
var binaryHelper = {
    blobToArrayBuffer: function blobToArrayBuffer(blob) {
        return new Promise(function (resolve, reject) {
            var reader = new FileReader();
            reader.addEventListener("loadend", function () {
                // reader.result contains the contents of blob as a typed array
                resolve(this.result);
            });
            reader.addEventListener("error", reject);
            reader.readAsArrayBuffer(blob);
        });
    },
    arrayBufferToBinary: function ArrayBufferToString(buffer) {
        var arr = new Uint8Array(buffer);
        return binaryHelper.fromCharCodes(arr, buffer.byteLength);
    },
    arrayBufferToString: function (buffer) {
        return binaryHelper.binaryToString(binaryHelper.arrayBufferToBinary(buffer));
    },
    /**
     * for nodejs
     * @param {ArrayBuffer} buffer
     * @returns {Buffer}
     */
    arrayToBuffer: function (buffer) {
        return new Buffer(new Uint8Array(buffer));
    },
    fromCharCodes: function (arr, len) {
        if (typeof(len) !== 'number') {
            len = arr.length;
        }
        var i = 0, str = "", fromCharCode = String.fromCharCode;
        for (; i < len; i++) {
            str += fromCharCode(arr[i]);
        }
        return str;
    },
    stringToArrayBuffer: function stringToArrayBuffer(string) {
        return binaryHelper.stringToUint8Array(string).buffer;
    },

    binaryToString: function binaryToString(binary) {
        var error;
        try {
            return decodeURIComponent(escape(binary));
        } catch (_error) {
            error = _error;
            if (error instanceof URIError) {
                return binary;
            } else {
                throw error;
            }
        }
    },

    stringToBinary: function stringToBinary(string) {
        var chars, code, i, isUCS2, len, _i;

        len = string.length;
        chars = [];
        isUCS2 = false;
        for (i = _i = 0; 0 <= len ? _i < len : _i > len; i = 0 <= len ? ++_i : --_i) {
            //code = String.prototype.charCodeAt.call(string, i);
            code = string.charCodeAt(i);
            if (code > 255) {
                isUCS2 = true;
                chars = null;
                break;
            } else {
                chars.push(code);
            }
        }
        if (isUCS2 === true) {
            return unescape(encodeURIComponent(string));
        } else {
            return chars && binaryHelper.fromCharCodes(chars);
        }
    },

    stringToUint8Array: function stringToUint8Array(string) {
        var binary, binLen, buffer, chars, i, _i;
        binary = binaryHelper.stringToBinary(string);
        binLen = binary.length;
        buffer = new ArrayBuffer(binLen);
        chars = new Uint8Array(buffer);
        for (i = _i = 0; 0 <= binLen ? _i < binLen : _i > binLen; i = 0 <= binLen ? ++_i : --_i) {
            //chars[i] = String.prototype.charCodeAt.call(binary, i);
            chars[i] = binary.charCodeAt(i);
        }
        return chars;
    },
    dataURLToBlob: function (dataURL) {
        var BASE64_MARKER = ';base64,', parts, contentType, raw, rawLength, uInt8Array;
        if (dataURL.indexOf(BASE64_MARKER) == -1) {
            parts = dataURL.split(',');
            contentType = parts[0].split(':')[1];
            raw = decodeURIComponent(parts[1]);

            return new Blob([raw], {type: contentType});
        }

        parts = dataURL.split(BASE64_MARKER);
        contentType = parts[0].split(':')[1];
        raw = window.atob(parts[1]);
        rawLength = raw.length;

        uInt8Array = new Uint8Array(rawLength);

        for (var i = 0; i < rawLength; ++i) {
            uInt8Array[i] = raw.charCodeAt(i);
        }

        return new Blob([uInt8Array], {type: contentType});
    }

};
export default binaryHelper;
<|endoftext|>


<commit_before>angular.module('leukemiapp')

    .factory('ModuleManagementService', function () {

        var service = {};

        service.activeModules = [];
        service.modules = {};

        for (moduleNumber = 0; moduleNumber < Modules.length; moduleNumber++) {
            var moduleName = Modules[moduleNumber].name;
            service.modules[moduleName] = true;
        }

        const handle = Meteor.subscribe('userSettings');
        const handle2 = Meteor.subscribe('customModules');
        Tracker.autorun(() => {
            const isReady = handle.ready();
            const isReady2 = handle2.ready();

            if (isReady && isReady2) {
                console.log(CustomModules.find().fetch());
                startup();
            }
        });

        service.toggleModule = (moduleName) => {
            if (!!Meteor.userId()) {
                if (moduleName !== undefined) {
                    setTimeout(Meteor.call('setActiveModule', moduleName, service.modules[moduleName]), 1000);
                }
            }

            setActiveModules();
        };

        function startup() {
            addCustomModules();

            userModules = UserSettings.findOne({});  //userId: Meteor.userId()
            console.log("USER MODULES of " + Meteor.userId() + " " + JSON.stringify(userModules));
            if (!!Meteor.userId()) {
                if (userModules === undefined) { // no settings => default settings (turn all modules on)
                    for (var module in service.modules) {
                        service.modules[module] = true;
                        console.log("userIDDD " + Meteor.userId());
                        if (!!Meteor.userId()) {
                            setTimeout(Meteor.call('setActiveModule', module, true), 1000);
                        }
                    }
                } else { // otherwise load existing settings
                    userModules = userModules.modules;
                    for (var module in service.modules) {
                        if (userModules.hasOwnProperty(module) && module !== undefined) {
                            service.modules[module] = userModules[module];
                        }
                    }
                }
            } else { // user not logged in, show all modules
                for (var module in service.modules) {
                    service.modules[module] = true;
                }
            }

            setActiveModules();
        }

        function addCustomModules() {
            CustomModules.find().forEach(function (item) {
                for (var step in item.wizard.steps) {
                    item.wizard.steps[step]["validation"] = () => true;
                    console.log("step", item.wizard.steps[step]);
                }

                if (!item["frontPage"]) {
                    var frontpage = {"iconUrl": "/question-mark.png", "barClass": "bar-positive"};
                    item["frontPage"] = frontpage;
                }

                Modules.push(item);
                service.modules[item.name] = true;
            });
        }

        function setActiveModules() {
            console.log('service.modules is ', service.modules);
            var activeModules = [];

            for (var module in service.modules) {
                if (service.modules.hasOwnProperty(module) && module !== undefined) {
                    if (service.modules[module] == true) {
                        activeModules.push(module);
                    }
                }
            }

            service.activeModules.length = 0;
            angular.extend(service.activeModules, activeModules);

            console.log('active modules are ', service.activeModules);
        }

        return service;
    })

    .factory('WizardState', function () {
        return {};
    })

    .factory('WizardStateAccessor', function (WizardState) {
        var service = {
            validateFunction: []
        };

        service.getRegistration = (type) => {
                return WizardState[type];
        };

        service.setRegistration = (type, registration) => {
                WizardState[type] = registration;
            };

        service.registerValidateFunction = (type, validateFunction) => {
                service.validateFunction[type] = validateFunction;
            };

        service.validate = (type, registration, from, to) => {
            if (registration && service.validateFunction[type]) {
                return service.validateFunction[type](registration, from?from-2:from, to?to-2:to);
            }

            return undefined;
        };

        return service;
    });
<commit_msg>New custom modules are by default hidden on the front page.<commit_after>angular.module('leukemiapp')

    .factory('ModuleManagementService', function () {

        var service = {};

        service.activeModules = [];
        service.modules = {};

        for (moduleNumber = 0; moduleNumber < Modules.length; moduleNumber++) {
            var moduleName = Modules[moduleNumber].name;
            service.modules[moduleName] = true;
        }

        const handle = Meteor.subscribe('userSettings');
        const handle2 = Meteor.subscribe('customModules');
        Tracker.autorun(() => {
            const isReady = handle.ready();
            const isReady2 = handle2.ready();

            if (isReady && isReady2) {
                console.log(CustomModules.find().fetch());
                startup();
            }
        });

        service.toggleModule = (moduleName) => {
            if (!!Meteor.userId()) {
                if (moduleName !== undefined) {
                    setTimeout(Meteor.call('setActiveModule', moduleName, service.modules[moduleName]), 1000);
                }
            }

            setActiveModules();
        };

        function startup() {
            addCustomModules();

            userModules = UserSettings.findOne({});  //userId: Meteor.userId()
            console.log("USER MODULES of " + Meteor.userId() + " " + JSON.stringify(userModules));
            if (!!Meteor.userId()) {
                if (userModules === undefined) { // no settings => default settings (turn all modules on)
                    for (var module in service.modules) {
                        service.modules[module] = true;
                        console.log("userID " + Meteor.userId());
                        if (!!Meteor.userId()) {
                            setTimeout(Meteor.call('setActiveModule', module, true), 1000);
                        }
                    }
                } else { // otherwise load existing settings
                    userModules = userModules.modules;
                    for (var module in service.modules) {
                        if (userModules.hasOwnProperty(module) && module !== undefined) {
                            service.modules[module] = userModules[module];
                        }
                    }
                }
            } else { // user not logged in, show all modules
                for (var module in service.modules) {
                    service.modules[module] = true;
                }
            }

            setActiveModules();
        }

        function addCustomModules() {
            CustomModules.find().forEach(function (item) {
                for (var step in item.wizard.steps) {
                    item.wizard.steps[step]["validation"] = () => true;
                    console.log("step", item.wizard.steps[step]);
                }

                if (!item["frontPage"]) {
                    var frontpage = {"iconUrl": "/question-mark.png", "barClass": "bar-positive"};
                    item["frontPage"] = frontpage;
                }

                Modules.push(item);
                service.modules[item.name] = false; // default value for newly created modules
            });
        }

        function setActiveModules() {
            console.log('service.modules is ', service.modules);
            var activeModules = [];

            for (var module in service.modules) {
                if (service.modules.hasOwnProperty(module) && module !== undefined) {
                    if (service.modules[module] == true) {
                        activeModules.push(module);
                    }
                }
            }

            service.activeModules.length = 0;
            angular.extend(service.activeModules, activeModules);

            console.log('active modules are ', service.activeModules);
        }

        return service;
    })

    .factory('WizardState', function () {
        return {};
    })

    .factory('WizardStateAccessor', function (WizardState) {
        var service = {
            validateFunction: []
        };

        service.getRegistration = (type) => {
                return WizardState[type];
        };

        service.setRegistration = (type, registration) => {
                WizardState[type] = registration;
            };

        service.registerValidateFunction = (type, validateFunction) => {
                service.validateFunction[type] = validateFunction;
            };

        service.validate = (type, registration, from, to) => {
            if (registration && service.validateFunction[type]) {
                return service.validateFunction[type](registration, from?from-2:from, to?to-2:to);
            }

            return undefined;
        };

        return service;
    });
<|endoftext|>


<commit_before>'use strict';

/*
  Block Table
*/

var _ = require('../lodash');
var $ = require('jquery');
var Block = require('../block');
var template = '' +
  '
' +
    '

' +
    '
' +
      '
' +
        '	' +
        '	' +
      '
' +
    '
' +
    '
' +
      '
' +
        '	' +
        '	' +
      '
' +
    '
' +
  '
';

function addCell(row, cellTag) {
    var tag_template = _.template('<<%= tag %>>');
    if (cellTag === undefined) {
        cellTag = tag_template(
            { tag: $(row).children().first().prop('tagName').toLowerCase() }
        );
    }
    $(row).append($(cellTag, { contenteditable: '' }));
}
function addColumnHandler(ev, scope) {
    ev.preventDefault();
    scope.find('tr').each(function() {
        addCell(this);
    });
}
function mergeCellHandler(ev, scope) {
    ev.preventDefault();
    var first = 1;
    var table = scope;
    table.find('td').each(function() {
        $(this).addClass('mergeable');
        if (undefined === $(this).attr('colspan')){
            $(this).attr('colspan', 1);
        }
    });
    $('tr').children('td').on('click', function(){
        if (first === 1){
            deleteHelper(ev, scope);
            if ($(this).next('td').index() !== -1){
                $(this).next('td').remove();
                var colspanValue = parseInt($(this).attr('colspan'));
                $(this).attr('colspan', colspanValue + 1);
            }
            first = first + 1;
            table.find('td').each(function(){
                $(this).removeClass('mergeable');
            });
        }
    });
}
function unMergeCellHandler(ev, scope){
    ev.preventDefault();
    var first = 1;
    var table = scope;
    table.find('td').each(function(){
        var colspanValue = parseInt($(this).attr('colspan'));
        if (colspanValue > 1){
            $(this).addClass('unmergeable');
        }

    });
    $('tr').children('td').on('click', function(){
        if (first === 1){
            deleteHelper(ev, scope);
            var colspanValue = parseInt($(this).attr('colspan'));
            if (colspanValue > 1){
                $(this).attr('colspan', colspanValue - 1);
                $(this).parent().append('	');
            }
            first = first + 1;
            table.find('td').each(function() {
                $(this).removeClass('unmergeable');
                $(this).attr('contenteditable','');
            });
        }
    });}
function deleteColumnHandler(ev, scope) {
    ev.preventDefault();
    scope.find('tr').each(function() {
        if ($(this).children().length > 1) {
            var colspanValue = parseInt($(this).children().last().attr('colspan'));
            if (colspanValue > 1){
                $(this).children().last().attr('colspan', colspanValue - 1);
            }
            else {
                $(this).children().last().remove();
            }
        }

    });
}
function addRowHandler(ev, scope) {
    var row = $('
');
    ev.preventDefault();
    scope.find('th').each(function() {
        addCell(row, '');
    });
    scope.find('tbody').append(row);
}
function deleteRowHandler(ev, block) {
    ev.preventDefault();
    if (block.find('tbody tr').length > 1) {
        block.find('tbody tr:last').remove();
    }
}
function changeTheme(ev, block) {
    var table = block.find('table');
    var themeSelector = $('.theme-selector select');
    themeSelector.on('change', function(){
        var themeClass = $(this).find(':selected').val();
        table.removeClass();
        table.addClass(themeClass);
    });
}
function addHelper(ev, block, content) {
  var table = block.find('table');
  table.before(content);
}
function deleteHelper(ev, block) {
  var helper = block.find('.helper');
  helper.remove();
}

module.exports = Block.extend({
    type: 'table',
    title: function() { return 'Table'; },
    icon_name: 'text table',
    scope: function(){
        this.getTextBlock().find('table');
    },
    controllable: true,
    controls_position: 'bottom',
    controls_visible: true,
    controls: [
        {
            slug: 'theme-toggle',
            eventTrigger: 'click',
            fn: function(e) {
                 e.preventDefault();
                 var block = this.getTextBlock();
                 changeTheme(e, block);
             },
            html: '
<select><option class="theme edt-theme-default"  value="edt-theme-default">' + i18n.t('blocks:table:default') + '</option> <option class="theme edt-theme-blue" value="edt-theme-blue">' + i18n.t('blocks:table:blue-theme') + '</option><option class="theme edt-theme-red" value="edt-theme-red">' + i18n.t('blocks:table:red-theme') + '</option></select>
'
        },
        {
            slug: 'add-column',
            icon: 'add',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                addColumnHandler(e, block);
            }
        },
        {
            slug: 'del-column',
            icon: 'bin1',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                deleteColumnHandler(e, block);
            }
        },
        {
            slug: 'add-row',
            icon: 'add col',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                addRowHandler(e, block);
            }
        },
        {
            slug: 'del-row',
            icon: 'bin2',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                deleteRowHandler(e, block);
            }
        },
        {
            slug: 'merge-cell',
            icon: 'add cell',
            fn: function(e) {
                e.preventDefault();
                var content= '' + i18n.t('blocks:table:helper-merge') + '';
                var block = this.getTextBlock();
                deleteHelper(e, block, content);
                addHelper(e, block, content);
                mergeCellHandler(e, block);
            }
        },
        {
            slug: 'unmerge-cell',
            icon: 'binopen',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                var content= '' + i18n.t('blocks:table:helper-unmerge') + '';
                deleteHelper(e, block, content);
                addHelper(e, block, content);
                unMergeCellHandler(e, block);
            }
        }

    ],

    editorHTML: function() {
        var editor_template = '
' + template + '
';
        return _.template(editor_template, this);
    },
    onBlockRender: function() {
        this.$table = this.getTextBlock().find('table');
    },
    loadData: function(data){
        this.getTextBlock().html(data.text, this.type);
    },
    toMarkdown: function(html) {
        function rowToMarkdown(row) {
        var cells = $(row).children(),
            md = cells.map(function() { return $(this).text(); }).get().join(' | ');
        if (cells[0].tagName === 'TH') {
            md += '\n';
            md += cells.map(function() { return '---'; }).get().join(' | ');
        }
        return md;
    }
    var markdown = $(html).find('tr').map(function(){
        return rowToMarkdown(this);
        }).get().join('\n');
        if ($(html).find('caption').text() !== '') {
        markdown += '\n[' + $(html).find('caption').text() + ']';
        }
        return markdown;
    },
    toHTML: function(markdown) {
        var html = $('

'),
        lines = markdown.split('\n'),
        caption_re = /\[(.*)\]/,
        lastline;
        lastline = lines[lines.length - 1];
        if (lastline.match(caption_re)) {
            html.find('caption').text(lastline.match(caption_re)[1]);
            lines = lines.slice(0, lines.length - 1);
        }
        // Add header row
        _.each(lines[0].split(' | '), function(content) {
            html.find('thead tr').append('' + content + '	');
        });
        // Add remaining rows
        _.each(lines.slice(2, lines.length), function(line) {
            var row = $('
');
            _.each(line.split(' | '), function(content) {
                row.append('' + content + '	');
            });
        html.find('tbody').append(row);
        });
    return html[0].outerHTML;
    },

    isEmpty: function() {
        return _.isEmpty(this.saveAndGetData().text);
    }

});
<commit_msg>Resolved issue when mergin cell with another merged cell<commit_after>'use strict';

/*
  Block Table
*/

var _ = require('../lodash');
var $ = require('jquery');
var Block = require('../block');
var template = '' +
  '
' +
    '

' +
    '
' +
      '
' +
        '	' +
        '	' +
      '
' +
    '
' +
    '
' +
      '
' +
        '	' +
        '	' +
      '
' +
    '
' +
  '
';

function addCell(row, cellTag) {
    var tag_template = _.template('<<%= tag %>>');
    if (cellTag === undefined) {
        cellTag = tag_template(
            { tag: $(row).children().first().prop('tagName').toLowerCase() }
        );
    }
    $(row).append($(cellTag, { contenteditable: '' }));
}
function addColumnHandler(ev, scope) {
    ev.preventDefault();
    scope.find('tr').each(function() {
        addCell(this);
    });
}
function mergeCellHandler(ev, scope) {
    ev.preventDefault();
    var first = 1;
    var table = scope;
    table.find('td').each(function() {
        $(this).addClass('mergeable');
        if (undefined === $(this).attr('colspan')){
            $(this).attr('colspan', 1);
        }
    });
    $('tr').children('td').on('click', function(){
        if (first === 1){
            deleteHelper(ev, scope);
            if ($(this).next('td').index() !== -1){
                var nextWidth = parseInt($(this).next('td').attr('colspan'));
                $(this).next('td').remove();
                var colspanValue = parseInt($(this).attr('colspan'));
                $(this).attr('colspan', colspanValue + nextWidth);
            }
            first = first + 1;
            table.find('td').each(function(){
                $(this).removeClass('mergeable');
            });
        }
    });
}
function unMergeCellHandler(ev, scope){
    ev.preventDefault();
    var first = 1;
    var table = scope;
    table.find('td').each(function(){
        var colspanValue = parseInt($(this).attr('colspan'));
        if (colspanValue > 1){
            $(this).addClass('unmergeable');
        }

    });
    $('tr').children('td').on('click', function(){
        if (first === 1){
            deleteHelper(ev, scope);
            var colspanValue = parseInt($(this).attr('colspan'));
            if (colspanValue > 1){
                $(this).attr('colspan', colspanValue - 1);
                $(this).parent().append('	');
            }
            first = first + 1;
            table.find('td').each(function() {
                $(this).removeClass('unmergeable');
                $(this).attr('contenteditable',  '');
            });
        }
    });}
function deleteColumnHandler(ev, scope) {
    ev.preventDefault();
    scope.find('tr').each(function() {
        if ($(this).children().length > 1) {
            var colspanValue = parseInt($(this).children().last().attr('colspan'));
            if (colspanValue > 1){
                $(this).children().last().attr('colspan', colspanValue - 1);
            }
            else {
                $(this).children().last().remove();
            }
        }

    });
}
function addRowHandler(ev, scope) {
    var row = $('
');
    ev.preventDefault();
    scope.find('th').each(function() {
        addCell(row, '');
    });
    scope.find('tbody').append(row);
}
function deleteRowHandler(ev, block) {
    ev.preventDefault();
    if (block.find('tbody tr').length > 1) {
        block.find('tbody tr:last').remove();
    }
}
function changeTheme(ev, block) {
    var table = block.find('table');
    var themeSelector = $('.theme-selector select');
    themeSelector.on('change', function(){
        var themeClass = $(this).find(':selected').val();
        table.removeClass();
        table.addClass(themeClass);
    });
}
function addHelper(ev, block, content) {
  var table = block.find('table');
  table.before(content);
}
function deleteHelper(ev, block) {
  var helper = block.find('.helper');
  helper.remove();
}

module.exports = Block.extend({
    type: 'table',
    title: function() { return 'Table'; },
    icon_name: 'text table',
    scope: function(){
        this.getTextBlock().find('table');
    },
    controllable: true,
    controls_position: 'bottom',
    controls_visible: true,
    controls: [
        {
            slug: 'theme-toggle',
            eventTrigger: 'click',
            fn: function(e) {
                 e.preventDefault();
                 var block = this.getTextBlock();
                 changeTheme(e, block);
             },
            html: '
<select><option class="theme edt-theme-default"  value="edt-theme-default">' + i18n.t('blocks:table:default') + '</option> <option class="theme edt-theme-blue" value="edt-theme-blue">' + i18n.t('blocks:table:blue-theme') + '</option><option class="theme edt-theme-red" value="edt-theme-red">' + i18n.t('blocks:table:red-theme') + '</option></select>
'
        },
        {
            slug: 'add-column',
            icon: 'add',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                addColumnHandler(e, block);
            }
        },
        {
            slug: 'del-column',
            icon: 'bin1',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                deleteColumnHandler(e, block);
            }
        },
        {
            slug: 'add-row',
            icon: 'add col',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                addRowHandler(e, block);
            }
        },
        {
            slug: 'del-row',
            icon: 'bin2',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                deleteRowHandler(e, block);
            }
        },
        {
            slug: 'merge-cell',
            icon: 'add cell',
            fn: function(e) {
                e.preventDefault();
                var content= '' + i18n.t('blocks:table:helper-merge') + '';
                var block = this.getTextBlock();
                deleteHelper(e, block, content);
                addHelper(e, block, content);
                mergeCellHandler(e, block);
            }
        },
        {
            slug: 'unmerge-cell',
            icon: 'binopen',
            fn: function(e) {
                e.preventDefault();
                var block = this.getTextBlock();
                var content = '' + i18n.t('blocks:table:helper-unmerge') + '';
                deleteHelper(e, block, content);
                addHelper(e, block, content);
                unMergeCellHandler(e, block);
            }
        }

    ],

    editorHTML: function() {
        var editor_template = '
' + template + '
';
        return _.template(editor_template, this);
    },
    onBlockRender: function() {
        this.$table = this.getTextBlock().find('table');
    },
    loadData: function(data){
        this.getTextBlock().html(data.text, this.type);
    },
    toMarkdown: function(html) {
        function rowToMarkdown(row) {
        var cells = $(row).children(),
            md = cells.map(function() { return $(this).text(); }).get().join(' | ');
        if (cells[0].tagName === 'TH') {
            md += '\n';
            md += cells.map(function() { return '---'; }).get().join(' | ');
        }
        return md;
    }
    var markdown = $(html).find('tr').map(function(){
        return rowToMarkdown(this);
        }).get().join('\n');
        if ($(html).find('caption').text() !== '') {
        markdown += '\n[' + $(html).find('caption').text() + ']';
        }
        return markdown;
    },
    toHTML: function(markdown) {
        var html = $('

'),
        lines = markdown.split('\n'),
        caption_re = /\[(.*)\]/,
        lastline;
        lastline = lines[lines.length - 1];
        if (lastline.match(caption_re)) {
            html.find('caption').text(lastline.match(caption_re)[1]);
            lines = lines.slice(0, lines.length - 1);
        }
        // Add header row
        _.each(lines[0].split(' | '), function(content) {
            html.find('thead tr').append('' + content + '	');
        });
        // Add remaining rows
        _.each(lines.slice(2, lines.length), function(line) {
            var row = $('
');
            _.each(line.split(' | '), function(content) {
                row.append('' + content + '	');
            });
        html.find('tbody').append(row);
        });
    return html[0].outerHTML;
    },

    isEmpty: function() {
        return _.isEmpty(this.saveAndGetData().text);
    }

});
<|endoftext|>


<commit_before>import React from 'react'
import AppBar from 'material-ui/lib/app-bar'
import GridList from 'material-ui/lib/grid-list/grid-list'
import GridTile from 'material-ui/lib/grid-list/grid-tile'
import LeftNav from 'material-ui/lib/left-nav'
import IconButton from 'material-ui/lib/icon-button'
import NavigationClose from 'material-ui/lib/svg-icons/navigation/close'
import Card from 'material-ui/lib/card/card'
import CardHeader from 'material-ui/lib/card/card-header'
import CardMedia from 'material-ui/lib/card/card-media'
import CardTitle from 'material-ui/lib/card/card-title'
import FlatButton from 'material-ui/lib/flat-button'
import Divider from 'material-ui/lib/divider'
import FontIcon from 'material-ui/lib/font-icon'
import FloatingActionButton from 'material-ui/lib/floating-action-button'
import ContentWeekend from 'material-ui/lib/svg-icons/content/weekend'
import ArrowBack from 'material-ui/lib/svg-icons/navigation/arrow-back'
import ChevronLeft from 'material-ui/lib/svg-icons/navigation/chevron-left'
import ChevronRight from 'material-ui/lib/svg-icons/navigation/chevron-right'
import TurnedIn from 'material-ui/lib/svg-icons/action/turned-in'
import ActionSearch from 'material-ui/lib/svg-icons/action/search'
import ToggleStar from 'material-ui/lib/svg-icons/toggle/star'
import ToggleStarBorder from 'material-ui/lib/svg-icons/toggle/star-border'
import ListItem from 'material-ui/lib/lists/list-item'
import TextField from 'material-ui/lib/text-field'
import CheckBox from 'material-ui/lib/checkbox'
import DropDownMenu from 'material-ui/lib/DropDownMenu'
import MenuItem from 'material-ui/lib/menus/menu-item'

export default class App extends React.Component {

  constructor(props) {
    super(props)

    this.state = {
      open: false,
      readingMode: false,
      currentComic: {
        id: '',
        name: ''
      },
      allcomics: [],
      comicChapters: [],
      comicPictures: [],
      chapterId: null,
      favorites: new Set(),
      category: 'SHOW_LATEST',
      searchFilter: () => true,
      categoryFilter: () => true
    }
  }

  _handleComicToggle = (currentComic) => {
    this.setState({
      currentComic: currentComic,
      open: true
    })

    fetch(`/api/comic_${currentComic.id}.json`)
      .then((res) => res.ok ? res.json() : [])
      .then((comicChapters) => {
        this.setState({
          comicChapters: comicChapters.reverse()
        })
      })
      .catch(() => {
        this.setState({
          comicChapters: []
        })
      })
  }

  _handleCloseLeftNav = () => {
    this.setState({
      open: false
    })
  }

  _downloadComicChapter = (comicId, chapterId) => {
    fetch(`/api/comic_${comicId}_${chapterId}.json`)
      .then((res) => res.ok ? res.json() : [])
      .then((comicPictures) => {
        this.setState({
          comicPictures: comicPictures,
          readingMode: true,
          open: false,
          chapterId: chapterId
        })
      })
      .catch((err) => {
        console.err(err)
      })
  }

  _getComicById = (id) => {
    return this.state.allcomics.find((element) => element.id === id)
  }

  _getComicCover = (id) => {
    return id ? `http://www.comicvip.com/pics/0/${id}.jpg` : ''
  }

  _openNavigation = () => {
    this.setState({
      open: true
    })
  }

  _closeNavigation = () => {
    this.setState({
      open: false
    })
  }

  _closeReadingMode = () => {
    this.setState({
      readingMode: false
    })
  }

  _previousChapter = () => {
    let chapters = this.state.comicChapters
    let index = chapters.findIndex((chapter) => (
      chapter.id === this.state.chapterId
    ))
    return chapters[index - 1]
  }

  _nextChapter = () => {
    let chapters = this.state.comicChapters
    let index = chapters.findIndex((chapter) => (
      chapter.id === this.state.chapterId
    ))
    return chapters[index + 1]
  }

  _toggleFavorite = (id) => {
    let favorites = this.state.favorites
    favorites.has(id) ? favorites.delete(id) : favorites.add(id)

    this.setState({
      favorites: favorites
    })
  }

  _onSearchTextChanged = (event) => {
    let regexp = new RegExp(event.target.value) || /.+/
    this.setState({
      searchFilter: function (comic) {
        return regexp.test(comic.name)
      }
    })

  }

  _onCategoryChanged = (event, index, category) => {
    let categoryFilter
    switch(category) {
      case 'SHOW_LATEST':
        categoryFilter = () => true
        break;
      case 'SHOW_FAVORITE':
        categoryFilter = (comic) => this.state.favorites.has(comic.id)
        break;
    }
    this.setState({
      category,
      categoryFilter
    })
  }

  componentDidMount = () => {
    fetch(`/api/allcomics.json`)
      .then((res) => res.ok ? res.json() : [])
      .then((allcomics) => {
        this.setState({
          allcomics: allcomics
        })
      })
      .catch((err) => {
        console.err(err)
      })
  }

  render() {
    const navWidth = this.state.open ? '300px' : '0px';
    const styles = {
      appBar: {
        position: 'fixed',
        margin: '0',
        top: 0,
        maxWidth: `calc(100% - ${navWidth})`,
        background: '#063047'
      },
      navAppBar: {
        position: 'fixed',
        margin: '0',
        top: 0,
        background: '#063047'
      },
      appBarTextInput: {
        marginTop: '8px',
        color: '#EEE'
      },
      appbarButton: {
        margin: 'auto',
        color: '#EEE'
      },
      allcomics: {
        margin: '80px 0',
        width: `calc(100% - ${navWidth})`
      },
      gridList: {
        display: 'flex',
        justifyContent: 'center',
        margin: '0 auto'
      },
      navContent: {
        margin: '80px 3%'
      },
      comicTile: {
        width: '150px',
        cursor: 'pointer'
      },
      tileTitle: `
        linear-gradient(
          to top,
          rgba(0,0,0,0.7) 0%,
          rgba(0,0,0,0.3) 70%,
          rgba(0,0,0,0) 100%
        )`
      ,
      chapters: {
        textAlign: 'center'
      },
      reading: {
        background: '#1b1b1b',
        textAlign: 'center'
      },
      picture: {
        margin: '10px 0',
        borderRadius: '8px'
      },
      previousChapter: {
        position: 'fixed',
        left: '1%',
        top: '50%',
        backgroundColor: '#123',
        color: '#333'
      },
      nextChapter: {
        position: 'fixed',
        right: '1%',
        top: '50%',
        backgroundColor: '#123'
      },
      favoriteTile: {
        cursor: 'pointer'
      }
    }

    return (

          <AppBar
            title="Comiz"
            style={styles.appBar}
            titleStyle={{ width: '60%' }}
            iconElementLeft={
              <IconButton onTouchTap={this._closeReadingMode}>
                { !this.state.readingMode ? <ContentWeekend /> : <ArrowBack /> }
              </IconButton>
            }
          >
            <DropDownMenu
              value={this.state.category}
              labelStyle={{ color: 'white' }}
              onChange={this._onCategoryChanged}>
              <MenuItem value={'SHOW_LATEST'} primaryText="Latest"/>
              <MenuItem value={'SHOW_FAVORITE'} primaryText="Favorite"/>
            </DropDownMenu>
            <TextField
              ref={'searchText'}
              style={{ margin: '7px 0', width: '200px' }}
              inputStyle={{ color: '#EEE' }}
              hintStyle={{ color: '#afafaf' }}
              hintText={<ActionSearch style={{ margin: 'auto' }} color="#afafaf" />}
              onChange={this._onSearchTextChanged}
            />
          </AppBar>
           {
              !this.state.readingMode ? (

                  <GridList
                    style={styles.gridList}
                    cols={0}
                    cellHeight={200}
                    padding={12}>
                    {
                      this.state.allcomics
                        .filter(this.state.categoryFilter)
                        .filter(this.state.searchFilter)
                        .map((comic) => (
                          <GridTile
                            style={styles.comicTile}
                            key={comic.id}
                            title={comic.name}
                            onClick={this._handleComicToggle.bind(this, comic)}
                            titleBackground={styles.tileTitle}>

                          </GridTile>
                        ))
                    }
                  </GridList>

              ) : (

                  {
                    this.state.comicPictures.map((pic, idx) => (

                    ))
                  }
                  {
                    !this._previousChapter() ? '' :
                      <FloatingActionButton
                        backgroundColor={styles.previousChapter.backgroundColor}
                        style={styles.previousChapter}
                        onTouchTap={this._downloadComicChapter.bind(this,
                          this.state.currentComic.id, this._previousChapter().id)}
                      >
                        <ChevronLeft />
                      </FloatingActionButton>
                  }
                  {
                    !this._nextChapter() ? '' :
                      <FloatingActionButton
                        backgroundColor={styles.nextChapter.backgroundColor}
                        style={styles.nextChapter}
                        onTouchTap={this._downloadComicChapter.bind(this,
                          this.state.currentComic.id, this._nextChapter().id)}
                      >
                        <ChevronRight />
                      </FloatingActionButton>
                  }

              )
          }

        <LeftNav width={300} openRight={true} open={this.state.open}>
          <AppBar
            style={styles.navAppBar}
            title={'About'}
            iconElementLeft={<IconButton><TurnedIn /></IconButton>}>
          </AppBar>

            {
              <Card>
                <CardMedia>

                </CardMedia>
                <ListItem
                  key={this.state.currentComic.id}
                  primaryText={this.state.currentComic.name}
                  secondaryText={'Author Name'}
                  disabled={true}
                  leftCheckbox={
                    <CheckBox
                      checked={this.state.favorites.has(this.state.currentComic.id)}
                      checkedIcon={<ToggleStar />}
                      unCheckedIcon={<ToggleStarBorder />}
                      onCheck={this._toggleFavorite.bind(this, this.state.currentComic.id)}
                    />
                  }
                />
                <Divider />

                {
                  this.state.comicChapters.map((chapter) => (
                    <FlatButton
                      key={chapter.id}
                      label={chapter.name}
                      onTouchTap={
                        this._downloadComicChapter.bind(this,
                          this.state.currentComic.id, chapter.id)
                      }/>
                  ))
                }

              </Card>
            }

        </LeftNav>

    )
  }
}
<commit_msg>Improve reading mode navigating button<commit_after>import React from 'react'
import AppBar from 'material-ui/lib/app-bar'
import GridList from 'material-ui/lib/grid-list/grid-list'
import GridTile from 'material-ui/lib/grid-list/grid-tile'
import LeftNav from 'material-ui/lib/left-nav'
import IconButton from 'material-ui/lib/icon-button'
import NavigationClose from 'material-ui/lib/svg-icons/navigation/close'
import Card from 'material-ui/lib/card/card'
import CardHeader from 'material-ui/lib/card/card-header'
import CardMedia from 'material-ui/lib/card/card-media'
import CardTitle from 'material-ui/lib/card/card-title'
import FlatButton from 'material-ui/lib/flat-button'
import Divider from 'material-ui/lib/divider'
import FontIcon from 'material-ui/lib/font-icon'
import FloatingActionButton from 'material-ui/lib/floating-action-button'
import ContentWeekend from 'material-ui/lib/svg-icons/content/weekend'
import ArrowBack from 'material-ui/lib/svg-icons/navigation/arrow-back'
import ChevronLeft from 'material-ui/lib/svg-icons/navigation/chevron-left'
import ChevronRight from 'material-ui/lib/svg-icons/navigation/chevron-right'
import TurnedIn from 'material-ui/lib/svg-icons/action/turned-in'
import ActionSearch from 'material-ui/lib/svg-icons/action/search'
import ToggleStar from 'material-ui/lib/svg-icons/toggle/star'
import ToggleStarBorder from 'material-ui/lib/svg-icons/toggle/star-border'
import ListItem from 'material-ui/lib/lists/list-item'
import TextField from 'material-ui/lib/text-field'
import CheckBox from 'material-ui/lib/checkbox'
import DropDownMenu from 'material-ui/lib/DropDownMenu'
import MenuItem from 'material-ui/lib/menus/menu-item'

export default class App extends React.Component {

  constructor(props) {
    super(props)

    this.state = {
      open: false,
      readingMode: false,
      currentComic: {
        id: '',
        name: ''
      },
      allcomics: [],
      comicChapters: [],
      comicPictures: [],
      chapterId: null,
      favorites: new Set(),
      category: 'SHOW_LATEST',
      searchFilter: () => true,
      categoryFilter: () => true
    }
  }

  _handleComicToggle = (currentComic) => {
    this.setState({
      currentComic: currentComic,
      open: true
    })

    fetch(`/api/comic_${currentComic.id}.json`)
      .then((res) => res.ok ? res.json() : [])
      .then((comicChapters) => {
        this.setState({
          comicChapters: comicChapters.reverse()
        })
      })
      .catch(() => {
        this.setState({
          comicChapters: []
        })
      })
  }

  _handleCloseLeftNav = () => {
    this.setState({
      open: false
    })
  }

  _downloadComicChapter = (comicId, chapterId) => {
    fetch(`/api/comic_${comicId}_${chapterId}.json`)
      .then((res) => res.ok ? res.json() : [])
      .then((comicPictures) => {
        this.setState({
          comicPictures: comicPictures,
          readingMode: true,
          open: false,
          chapterId: chapterId
        })
      })
      .catch((err) => {
        console.err(err)
      })
  }

  _getComicById = (id) => {
    return this.state.allcomics.find((element) => element.id === id)
  }

  _getComicCover = (id) => {
    return id ? `http://www.comicvip.com/pics/0/${id}.jpg` : ''
  }

  _openNavigation = () => {
    this.setState({
      open: true
    })
  }

  _closeNavigation = () => {
    this.setState({
      open: false
    })
  }

  _closeReadingMode = () => {
    this.setState({
      readingMode: false
    })
  }

  _previousChapter = () => {
    let chapters = this.state.comicChapters
    let index = chapters.findIndex((chapter) => (
      chapter.id === this.state.chapterId
    ))
    return chapters[index - 1]
  }

  _nextChapter = () => {
    let chapters = this.state.comicChapters
    let index = chapters.findIndex((chapter) => (
      chapter.id === this.state.chapterId
    ))
    return chapters[index + 1]
  }

  _toggleFavorite = (id) => {
    let favorites = this.state.favorites
    favorites.has(id) ? favorites.delete(id) : favorites.add(id)

    this.setState({
      favorites: favorites
    })
  }

  _onSearchTextChanged = (event) => {
    let regexp = new RegExp(event.target.value) || /.+/
    this.setState({
      searchFilter: function (comic) {
        return regexp.test(comic.name)
      }
    })

  }

  _onCategoryChanged = (event, index, category) => {
    let categoryFilter
    switch(category) {
      case 'SHOW_LATEST':
        categoryFilter = () => true
        break;
      case 'SHOW_FAVORITE':
        categoryFilter = (comic) => this.state.favorites.has(comic.id)
        break;
    }
    this.setState({
      category,
      categoryFilter
    })
  }

  componentDidMount = () => {
    fetch(`/api/allcomics.json`)
      .then((res) => res.ok ? res.json() : [])
      .then((allcomics) => {
        this.setState({
          allcomics: allcomics
        })
      })
      .catch((err) => {
        console.err(err)
      })
  }

  render() {
    const navWidth = this.state.open ? '300px' : '0px';
    const styles = {
      appBar: {
        position: 'fixed',
        margin: '0',
        top: 0,
        maxWidth: `calc(100% - ${navWidth})`,
        background: '#063047'
      },
      navAppBar: {
        position: 'fixed',
        margin: '0',
        top: 0,
        background: '#063047'
      },
      appBarTextInput: {
        marginTop: '8px',
        color: '#EEE'
      },
      appbarButton: {
        margin: 'auto',
        color: '#EEE'
      },
      allcomics: {
        margin: '80px 0',
        width: `calc(100% - ${navWidth})`
      },
      gridList: {
        display: 'flex',
        justifyContent: 'center',
        margin: '0 auto'
      },
      navContent: {
        margin: '80px 3%'
      },
      comicTile: {
        width: '150px',
        cursor: 'pointer'
      },
      tileTitle: `
        linear-gradient(
          to top,
          rgba(0,0,0,0.7) 0%,
          rgba(0,0,0,0.3) 70%,
          rgba(0,0,0,0) 100%
        )`
      ,
      chapters: {
        textAlign: 'center'
      },
      reading: {
        background: '#1b1b1b',
        textAlign: 'center'
      },
      picture: {
        margin: '10px 0',
        borderRadius: '8px'
      },
      previousChapter: {
        position: 'fixed',
        left: '1%',
        top: '10%',
        height: '80%',
        backgroundColor: 'rgba(#112233, 0.24)'
      },
      nextChapter: {
        position: 'fixed',
        right: '1%',
        top: '10%',
        height: '80%',
        backgroundColor: 'rgba(#112233, 0.24)'
      },
      favoriteTile: {
        cursor: 'pointer'
      }
    }

    return (

          <AppBar
            title="Comiz"
            style={styles.appBar}
            titleStyle={{ width: '60%' }}
            iconElementLeft={
              <IconButton onTouchTap={this._closeReadingMode}>
                { !this.state.readingMode ? <ContentWeekend /> : <ArrowBack /> }
              </IconButton>
            }
          >
            <DropDownMenu
              value={this.state.category}
              labelStyle={{ color: 'white' }}
              onChange={this._onCategoryChanged}>
              <MenuItem value={'SHOW_LATEST'} primaryText="Latest"/>
              <MenuItem value={'SHOW_FAVORITE'} primaryText="Favorite"/>
            </DropDownMenu>
            <TextField
              ref={'searchText'}
              style={{ margin: '7px 0', width: '200px' }}
              inputStyle={{ color: '#EEE' }}
              hintStyle={{ color: '#afafaf' }}
              hintText={<ActionSearch style={{ margin: 'auto' }} color="#afafaf" />}
              onChange={this._onSearchTextChanged}
            />
          </AppBar>
           {
              !this.state.readingMode ? (

                  <GridList
                    style={styles.gridList}
                    cols={0}
                    cellHeight={200}
                    padding={12}>
                    {
                      this.state.allcomics
                        .filter(this.state.categoryFilter)
                        .filter(this.state.searchFilter)
                        .map((comic) => (
                          <GridTile
                            style={styles.comicTile}
                            key={comic.id}
                            title={comic.name}
                            onClick={this._handleComicToggle.bind(this, comic)}
                            titleBackground={styles.tileTitle}>

                          </GridTile>
                        ))
                    }
                  </GridList>

              ) : (

                  {
                    this.state.comicPictures.map((pic, idx) => (

                    ))
                  }
                  {
                    !this._previousChapter() ? '' :
                      <IconButton
                        style={styles.previousChapter}
                        onTouchTap={this._downloadComicChapter.bind(this,
                          this.state.currentComic.id, this._previousChapter().id)}
                      >
                        <ChevronLeft color={'#ffffff'} />
                      </IconButton>
                  }
                  {
                    !this._nextChapter() ? '' :
                    <IconButton
                      style={styles.nextChapter}
                      onTouchTap={this._downloadComicChapter.bind(this,
                        this.state.currentComic.id, this._nextChapter().id)}
                    >
                      <ChevronRight color={'#ffffff'} />
                    </IconButton>
                  }

              )
          }

        <LeftNav width={300} openRight={true} open={this.state.open}>
          <AppBar
            style={styles.navAppBar}
            title={'About'}
            iconElementLeft={<IconButton><TurnedIn /></IconButton>}>
          </AppBar>

            {
              <Card>
                <CardMedia>

                </CardMedia>
                <ListItem
                  key={this.state.currentComic.id}
                  primaryText={this.state.currentComic.name}
                  secondaryText={'Author Name'}
                  disabled={true}
                  leftCheckbox={
                    <CheckBox
                      checked={this.state.favorites.has(this.state.currentComic.id)}
                      checkedIcon={<ToggleStar />}
                      unCheckedIcon={<ToggleStarBorder />}
                      onCheck={this._toggleFavorite.bind(this, this.state.currentComic.id)}
                    />
                  }
                />
                <Divider />

                {
                  this.state.comicChapters.map((chapter) => (
                    <FlatButton
                      key={chapter.id}
                      label={chapter.name}
                      onTouchTap={
                        this._downloadComicChapter.bind(this,
                          this.state.currentComic.id, chapter.id)
                      }/>
                  ))
                }

              </Card>
            }

        </LeftNav>

    )
  }
}
<|endoftext|>


<commit_before>import { Float32BufferAttribute } from '../core/BufferAttribute.js';
import { BufferGeometry } from '../core/BufferGeometry.js';
import { Object3D } from '../core/Object3D.js';
import { CylinderGeometry } from '../geometries/CylinderGeometry.js';
import { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js';
import { LineBasicMaterial } from '../materials/LineBasicMaterial.js';
import { Mesh } from '../objects/Mesh.js';
import { Line } from '../objects/Line.js';
import { Vector3 } from '../math/Vector3.js';

const _axis = /*@__PURE__*/ new Vector3();
let _lineGeometry, _coneGeometry;

class ArrowHelper extends Object3D {

	constructor( dir, origin, length, color, headLength, headWidth ) {

		super();
		// dir is assumed to be normalized

		this.type = 'ArrowHelper';

		if ( dir === undefined ) dir = new Vector3( 0, 0, 1 );
		if ( origin === undefined ) origin = new Vector3( 0, 0, 0 );
		if ( length === undefined ) length = 1;
		if ( color === undefined ) color = 0xffff00;
		if ( headLength === undefined ) headLength = 0.2 * length;
		if ( headWidth === undefined ) headWidth = 0.2 * headLength;

		if ( _lineGeometry === undefined ) {

			_lineGeometry = new BufferGeometry();
			_lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );

			_coneGeometry = new CylinderGeometry( 0, 0.5, 1, 5, 1 );
			_coneGeometry.translate( 0, - 0.5, 0 );

		}

		this.position.copy( origin );

		this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
		this.line.matrixAutoUpdate = false;
		this.add( this.line );

		this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) );
		this.cone.matrixAutoUpdate = false;
		this.add( this.cone );

		this.setDirection( dir );
		this.setLength( length, headLength, headWidth );

	}

	setDirection( dir ) {

		// dir is assumed to be normalized

		if ( dir.y > 0.99999 ) {

			this.quaternion.set( 0, 0, 0, 1 );

		} else if ( dir.y < - 0.99999 ) {

			this.quaternion.set( 1, 0, 0, 0 );

		} else {

			_axis.set( dir.z, 0, - dir.x ).normalize();

			const radians = Math.acos( dir.y );

			this.quaternion.setFromAxisAngle( _axis, radians );

		}

	}

	setLength( length, headLength, headWidth ) {

		if ( headLength === undefined ) headLength = 0.2 * length;
		if ( headWidth === undefined ) headWidth = 0.2 * headLength;

		this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
		this.line.updateMatrix();

		this.cone.scale.set( headWidth, headLength, headWidth );
		this.cone.position.y = length;
		this.cone.updateMatrix();

	}

	setColor( color ) {

		this.line.material.color.set( color );
		this.cone.material.color.set( color );

	}

	copy( source ) {

		super.copy( source, false );

		this.line.copy( source.line );
		this.cone.copy( source.cone );

		return this;

	}

}

export { ArrowHelper };
<commit_msg>ArrowHelper: Implemented default constructor parameters.<commit_after>import { Float32BufferAttribute } from '../core/BufferAttribute.js';
import { BufferGeometry } from '../core/BufferGeometry.js';
import { Object3D } from '../core/Object3D.js';
import { CylinderGeometry } from '../geometries/CylinderGeometry.js';
import { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js';
import { LineBasicMaterial } from '../materials/LineBasicMaterial.js';
import { Mesh } from '../objects/Mesh.js';
import { Line } from '../objects/Line.js';
import { Vector3 } from '../math/Vector3.js';

const _axis = /*@__PURE__*/ new Vector3();
let _lineGeometry, _coneGeometry;

class ArrowHelper extends Object3D {

	// dir is assumed to be normalized

	constructor( dir = new Vector3( 0, 0, 1 ), origin = new Vector3( 0, 0, 0 ), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2 ) {

		super();

		this.type = 'ArrowHelper';

		if ( _lineGeometry === undefined ) {

			_lineGeometry = new BufferGeometry();
			_lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );

			_coneGeometry = new CylinderGeometry( 0, 0.5, 1, 5, 1 );
			_coneGeometry.translate( 0, - 0.5, 0 );

		}

		this.position.copy( origin );

		this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
		this.line.matrixAutoUpdate = false;
		this.add( this.line );

		this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) );
		this.cone.matrixAutoUpdate = false;
		this.add( this.cone );

		this.setDirection( dir );
		this.setLength( length, headLength, headWidth );

	}

	setDirection( dir ) {

		// dir is assumed to be normalized

		if ( dir.y > 0.99999 ) {

			this.quaternion.set( 0, 0, 0, 1 );

		} else if ( dir.y < - 0.99999 ) {

			this.quaternion.set( 1, 0, 0, 0 );

		} else {

			_axis.set( dir.z, 0, - dir.x ).normalize();

			const radians = Math.acos( dir.y );

			this.quaternion.setFromAxisAngle( _axis, radians );

		}

	}

	setLength( length, headLength = length * 0.2, headWidth = headLength * 0.2 ) {

		this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
		this.line.updateMatrix();

		this.cone.scale.set( headWidth, headLength, headWidth );
		this.cone.position.y = length;
		this.cone.updateMatrix();

	}

	setColor( color ) {

		this.line.material.color.set( color );
		this.cone.material.color.set( color );

	}

	copy( source ) {

		super.copy( source, false );

		this.line.copy( source.line );
		this.cone.copy( source.cone );

		return this;

	}

}

export { ArrowHelper };
<|endoftext|>