<commit_before>// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

angular.module('mm.addons.mod_scorm')

/**
 * SCORM player controller.
 *
 * @module mm.addons.mod_scorm
 * @ngdoc controller
 * @name mmaModScormPlayerCtrl
 */
.controller('mmaModScormPlayerCtrl', function($scope, $stateParams, $mmaModScorm, $mmUtil, $ionicPopover, $mmaModScormHelper,
            $mmEvents, $timeout, $q, mmaModScormEventUpdateToc, mmaModScormEventLaunchNextSco, mmaModScormEventLaunchPrevSco,
            $mmaModScormDataModel12, mmaModScormEventGoOffline) {

    var scorm = $stateParams.scorm || {},
        mode = $stateParams.mode || $mmaModScorm.MODENORMAL,
        newAttempt = $stateParams.newAttempt,
        organizationId = $stateParams.organizationId,
        currentSco,
        attempt,
        userData,
        apiInitialized = false,
        offline = false;

    $scope.title = scorm.name; // We use SCORM name at start, later we'll use the SCO title.
    $scope.description = scorm.intro;
    $scope.scorm = scorm;
    $scope.loadingToc = true;

    // Fetch data needed to play the SCORM.
    function fetchData() {
        // Get attempts data.
        return $mmaModScorm.getAttemptCount(scorm.id).then(function(attemptsData) {
            return determineAttemptAndMode(attemptsData).then(function() {
                // Fetch TOC and get user data.
                var promises = [];
                promises.push(fetchToc());
                promises.push($mmaModScorm.getScormUserData(scorm.id, attempt, offline).then(function(data) {
                    userData = data;
                }));

                return $q.all(promises);
            });
        }).catch(showError);
    }

    // Determine the attempt to use, the mode (normal/preview) and if it's offline or online.
    function determineAttemptAndMode(attemptsData) {
        attempt = attemptsData.lastAttempt.number;
        offline = attemptsData.lastAttempt.offline;

        // Check if current attempt is incomplete.
        var promise;
        if (attempt > 0) {
            promise = $mmaModScorm.isAttemptIncomplete(scorm.id, attempt, offline);
        } else {
            // User doesn't have attempts. Last attempt is not incomplete (since he doesn't have any).
            promise = $q.when(false);
        }

        return promise.then(function(incomplete) {
            // Determine mode and attempt to use.
            var result = $mmaModScorm.determineAttemptAndMode(scorm, mode, attempt, newAttempt, incomplete);

            if (result.attempt > attempt) {
                // We're creating a new attempt.
                if (offline) {
                    // Last attempt was offline, so we'll create a new offline attempt.
                    promise = $mmaModScormHelper.createOfflineAttempt(scorm, result.attempt, attemptsData.online.length);
                } else {
                    // Last attempt was online, verify that we can create a new online attempt.
                    promise = $mmaModScorm.getScormUserData(scorm.id, result.attempt).catch(function() {
                        // Cannot communicate with the server, create an offline attempt.
                        offline = true;
                        return $mmaModScormHelper.createOfflineAttempt(scorm, result.attempt, attemptsData.online.length);
                    });
                }
            } else {
                promise = $q.when();
            }

            return promise.then(function() {
                mode = result.mode;
                newAttempt = result.newAttempt;
                attempt = result.attempt;
                $scope.isBrowse = mode === $mmaModScorm.MODEBROWSE;
                $scope.isReview = mode === $mmaModScorm.MODEREVIEW;
            });
        });
    }

    // Show error and reject.
    function showError(message) {
        if (message) {
            $mmUtil.showErrorModal(message);
        } else {
            $mmUtil.showErrorModal('mma.mod_scorm.errorgetscorm', true);
        }
        return $q.reject();
    }

    // Fetch TOC.
    function fetchToc() {
        $scope.loadingToc = true;
        // We need to check incomplete again: attempt number might have changed in determineAttemptAndMode,
        // or attempt status might have changed due to an action in the current SCO.
        return $mmaModScorm.isAttemptIncomplete(scorm.id, attempt, offline).then(function(incomplete) {
            scorm.incomplete = incomplete;

            // Get TOC.
            return $mmaModScorm.getOrganizationToc(scorm.id, organizationId, attempt, offline).then(function(toc) {
                $scope.toc = $mmaModScorm.formatTocToArray(toc);
                // Get images for each SCO.
                angular.forEach($scope.toc, function(sco) {
                    sco.image = $mmaModScorm.getScoStatusIcon(sco, scorm.incomplete);
                });
                // Determine current SCO if param is set.
                if ($stateParams.scoId > 0) {
                    // SCO set by parameter, get it from TOC.
                    currentSco = $mmaModScormHelper.getScoFromToc($scope.toc, $stateParams.scoId);
                }

                if (!currentSco) {
                    // No SCO defined. Get the first valid one.
                    return $mmaModScormHelper.getFirstSco(scorm.id, $scope.toc, organizationId, attempt, offline)
                            .then(function(sco) {
                        if (sco) {
                            currentSco = sco;
                        } else {
                            // We couldn't find a SCO to load: they're all inactive or without launch URL.
                            $scope.errorMessage = 'mma.mod_scorm.errornovalidsco';
                        }
                    });
                }
            });
        }).finally(function() {
            $scope.loadingToc = false;
        });
    }

    // Calculate the next and previous SCO.
    function calculateNextAndPreviousSco(scoId) {
        $scope.previousSco = $mmaModScormHelper.getPreviousScoFromToc($scope.toc, scoId);
        $scope.nextSco = $mmaModScormHelper.getNextScoFromToc($scope.toc, scoId);
    }

    // Load a SCO.
    function loadSco(sco) {
        // Setup API.
        if (!apiInitialized) {
            $mmaModScormDataModel12.initAPI(scorm, sco.id, attempt, userData, mode, offline);
            apiInitialized = true;
        } else {
            $mmaModScormDataModel12.loadSco(sco.id);
        }

        currentSco = sco;
        $scope.title = sco.title || scorm.name; // Try to use SCO title.
        calculateNextAndPreviousSco(sco.id);
        $mmaModScorm.getScoSrc(scorm, sco).then(function(src) {
            if ($scope.src && src.toString() == $scope.src.toString()) {
                // Re-loading same page. Set it to empty and then re-set the src in the next digest so it detects it has changed.
                $scope.src = '';
                $timeout(function() {
                    $scope.src = src;
                });
            } else {
                $scope.src = src;
            }
        });

        if (sco.scormtype == 'asset') {
            // Mark the asset as completed.
            var tracks = [{
                element: 'cmi.core.lesson_status',
                value: 'completed'
            }];
            $mmaModScorm.saveTracks(sco.id, attempt, tracks, offline, scorm).catch(function() {
                // Error saving data. We'll go offline if we're online and the asset is not marked as completed already.
                if (!offline) {
                    return $mmaModScorm.getScormUserData(scorm.id, attempt, offline).then(function(data) {
                        if (!data[sco.id] || data[sco.id].userdata['cmi.core.lesson_status'] != 'completed') {
                            // Go offline.
                            return $mmaModScormHelper.convertAttemptToOffline(scorm, attempt).then(function() {
                                offline = true;
                                $mmaModScormDataModel12.setOffline(true);
                                return $mmaModScorm.saveTracks(sco.id, attempt, tracks, offline, scorm);
                            }).catch(showError);
                        }
                    });
                }
            }).then(function() {
                // Refresh TOC, some prerequisites might have changed.
                refreshToc();
            });
        }
    }

    // Refresh the TOC.
    function refreshToc() {
        $mmaModScorm.invalidateAllScormData(scorm.id).finally(function() {
            fetchToc().catch(showError);
        });
    }

    // Set SCORM start time.
    function setStartTime(scoId) {
        var tracks = [{
            element: 'x.start.time',
            value: $mmUtil.timestamp()
        }];
        return $mmaModScorm.saveTracks(scoId, attempt, tracks, offline, scorm);
    }

    $scope.showToc = $mmaModScorm.displayTocInPlayer(scorm);
    if ($scope.showToc) {
        // Setup TOC popover.
        $ionicPopover.fromTemplateUrl('addons/mod_scorm/templates/toc.html', {
            scope: $scope,
        }).then(function(popover) {
            $scope.popover = popover;
        });
    }

    // Fetch the SCORM data.
    fetchData().then(function() {
        if (currentSco) {
            // Set start time if it's a new attempt.
            var promise = newAttempt ? setStartTime(currentSco.id) : $q.when();
            return promise.catch(showError).finally(function() {
                // Load SCO.
                loadSco(currentSco);
            });
        }
    }).finally(function() {
        $scope.loaded = true;
    });

    $scope.loadSco = function(sco) {
        if (!sco.prereq || !sco.isvisible || !sco.launch) {
            return;
        }

        $scope.popover.hide();
        loadSco(sco);
    };

    // Listen for events to update the TOC and navigate through SCOs.
    var tocObserver = $mmEvents.on(mmaModScormEventUpdateToc, function(data) {
        if (data.scormid === scorm.id) {
            if (offline) {
                // Wait a bit to make sure data is stored.
                $timeout(refreshToc, 100);
            } else {
                refreshToc();
            }
        }
    });

    var launchNextObserver = $mmEvents.on(mmaModScormEventLaunchNextSco, function(data) {
        if (data.scormid === scorm.id && $scope.nextSco) {
            loadSco($scope.nextSco);
        }
    });

    var launchPrevObserver = $mmEvents.on(mmaModScormEventLaunchPrevSco, function(data) {
        if (data.scormid === scorm.id && $scope.previousSco) {
            loadSco($scope.previousSco);
        }
    });

    var goOfflineObserver = $mmEvents.on(mmaModScormEventGoOffline, function(data) {
        if (data.scormid === scorm.id && !offline) {
            offline = true;
            $timeout(function() {
                // Wait a bit to prevent collisions between this store and SCORM API's store.
                $mmaModScormHelper.convertAttemptToOffline(scorm, attempt).catch(showError).finally(function() {
                    refreshToc();
                });
            }, 500);
        }
    });

    $scope.$on('$destroy', function() {
        tocObserver && tocObserver.off && tocObserver.off();
        launchNextObserver && launchNextObserver.off && launchNextObserver.off();
        launchPrevObserver && launchPrevObserver.off && launchPrevObserver.off();
        goOfflineObserver && goOfflineObserver.off && goOfflineObserver.off();
    });
});
<commit_msg>MOBILE-1354 scorm: Re-fetch attempts count when creating new online<commit_after>// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

angular.module('mm.addons.mod_scorm')

/**
 * SCORM player controller.
 *
 * @module mm.addons.mod_scorm
 * @ngdoc controller
 * @name mmaModScormPlayerCtrl
 */
.controller('mmaModScormPlayerCtrl', function($scope, $stateParams, $mmaModScorm, $mmUtil, $ionicPopover, $mmaModScormHelper,
            $mmEvents, $timeout, $q, mmaModScormEventUpdateToc, mmaModScormEventLaunchNextSco, mmaModScormEventLaunchPrevSco,
            $mmaModScormDataModel12, mmaModScormEventGoOffline) {

    var scorm = $stateParams.scorm || {},
        mode = $stateParams.mode || $mmaModScorm.MODENORMAL,
        newAttempt = $stateParams.newAttempt,
        organizationId = $stateParams.organizationId,
        currentSco,
        attempt,
        userData,
        apiInitialized = false,
        offline = false;

    $scope.title = scorm.name; // We use SCORM name at start, later we'll use the SCO title.
    $scope.description = scorm.intro;
    $scope.scorm = scorm;
    $scope.loadingToc = true;

    // Fetch data needed to play the SCORM.
    function fetchData() {
        // Get attempts data.
        return $mmaModScorm.getAttemptCount(scorm.id).then(function(attemptsData) {
            return determineAttemptAndMode(attemptsData).then(function() {
                // Fetch TOC and get user data.
                var promises = [];
                promises.push(fetchToc());
                promises.push($mmaModScorm.getScormUserData(scorm.id, attempt, offline).then(function(data) {
                    userData = data;
                }));

                return $q.all(promises);
            });
        }).catch(showError);
    }

    // Determine the attempt to use, the mode (normal/preview) and if it's offline or online.
    function determineAttemptAndMode(attemptsData) {
        attempt = attemptsData.lastAttempt.number;
        offline = attemptsData.lastAttempt.offline;

        // Check if current attempt is incomplete.
        var promise;
        if (attempt > 0) {
            promise = $mmaModScorm.isAttemptIncomplete(scorm.id, attempt, offline);
        } else {
            // User doesn't have attempts. Last attempt is not incomplete (since he doesn't have any).
            promise = $q.when(false);
        }

        return promise.then(function(incomplete) {
            // Determine mode and attempt to use.
            var result = $mmaModScorm.determineAttemptAndMode(scorm, mode, attempt, newAttempt, incomplete);

            if (result.attempt > attempt) {
                // We're creating a new attempt.
                if (offline) {
                    // Last attempt was offline, so we'll create a new offline attempt.
                    promise = $mmaModScormHelper.createOfflineAttempt(scorm, result.attempt, attemptsData.online.length);
                } else {
                    // Last attempt was online, verify that we can create a new online attempt.
                    promise = $mmaModScorm.getScormUserData(scorm.id, result.attempt).catch(function() {
                        // Cannot communicate with the server, create an offline attempt.
                        offline = true;
                        return $mmaModScormHelper.createOfflineAttempt(scorm, result.attempt, attemptsData.online.length);
                    });
                }
            } else {
                promise = $q.when();
            }

            return promise.then(function() {
                mode = result.mode;
                newAttempt = result.newAttempt;
                attempt = result.attempt;
                $scope.isBrowse = mode === $mmaModScorm.MODEBROWSE;
                $scope.isReview = mode === $mmaModScorm.MODEREVIEW;
            });
        });
    }

    // Show error and reject.
    function showError(message) {
        if (message) {
            $mmUtil.showErrorModal(message);
        } else {
            $mmUtil.showErrorModal('mma.mod_scorm.errorgetscorm', true);
        }
        return $q.reject();
    }

    // Fetch TOC.
    function fetchToc() {
        $scope.loadingToc = true;
        // We need to check incomplete again: attempt number might have changed in determineAttemptAndMode,
        // or attempt status might have changed due to an action in the current SCO.
        return $mmaModScorm.isAttemptIncomplete(scorm.id, attempt, offline).then(function(incomplete) {
            scorm.incomplete = incomplete;

            // Get TOC.
            return $mmaModScorm.getOrganizationToc(scorm.id, organizationId, attempt, offline).then(function(toc) {
                $scope.toc = $mmaModScorm.formatTocToArray(toc);
                // Get images for each SCO.
                angular.forEach($scope.toc, function(sco) {
                    sco.image = $mmaModScorm.getScoStatusIcon(sco, scorm.incomplete);
                });
                // Determine current SCO if param is set.
                if ($stateParams.scoId > 0) {
                    // SCO set by parameter, get it from TOC.
                    currentSco = $mmaModScormHelper.getScoFromToc($scope.toc, $stateParams.scoId);
                }

                if (!currentSco) {
                    // No SCO defined. Get the first valid one.
                    return $mmaModScormHelper.getFirstSco(scorm.id, $scope.toc, organizationId, attempt, offline)
                            .then(function(sco) {
                        if (sco) {
                            currentSco = sco;
                        } else {
                            // We couldn't find a SCO to load: they're all inactive or without launch URL.
                            $scope.errorMessage = 'mma.mod_scorm.errornovalidsco';
                        }
                    });
                }
            });
        }).finally(function() {
            $scope.loadingToc = false;
        });
    }

    // Calculate the next and previous SCO.
    function calculateNextAndPreviousSco(scoId) {
        $scope.previousSco = $mmaModScormHelper.getPreviousScoFromToc($scope.toc, scoId);
        $scope.nextSco = $mmaModScormHelper.getNextScoFromToc($scope.toc, scoId);
    }

    // Load a SCO.
    function loadSco(sco) {
        // Setup API.
        if (!apiInitialized) {
            $mmaModScormDataModel12.initAPI(scorm, sco.id, attempt, userData, mode, offline);
            apiInitialized = true;
        } else {
            $mmaModScormDataModel12.loadSco(sco.id);
        }

        currentSco = sco;
        $scope.title = sco.title || scorm.name; // Try to use SCO title.
        calculateNextAndPreviousSco(sco.id);
        $mmaModScorm.getScoSrc(scorm, sco).then(function(src) {
            if ($scope.src && src.toString() == $scope.src.toString()) {
                // Re-loading same page. Set it to empty and then re-set the src in the next digest so it detects it has changed.
                $scope.src = '';
                $timeout(function() {
                    $scope.src = src;
                });
            } else {
                $scope.src = src;
            }
        });

        if (sco.scormtype == 'asset') {
            // Mark the asset as completed.
            var tracks = [{
                element: 'cmi.core.lesson_status',
                value: 'completed'
            }];
            $mmaModScorm.saveTracks(sco.id, attempt, tracks, offline, scorm).catch(function() {
                // Error saving data. We'll go offline if we're online and the asset is not marked as completed already.
                if (!offline) {
                    return $mmaModScorm.getScormUserData(scorm.id, attempt, offline).then(function(data) {
                        if (!data[sco.id] || data[sco.id].userdata['cmi.core.lesson_status'] != 'completed') {
                            // Go offline.
                            return $mmaModScormHelper.convertAttemptToOffline(scorm, attempt).then(function() {
                                offline = true;
                                $mmaModScormDataModel12.setOffline(true);
                                return $mmaModScorm.saveTracks(sco.id, attempt, tracks, offline, scorm);
                            }).catch(showError);
                        }
                    });
                }
            }).then(function() {
                // Refresh TOC, some prerequisites might have changed.
                refreshToc();
            });
        }
    }

    // Refresh the TOC.
    function refreshToc() {
        $mmaModScorm.invalidateAllScormData(scorm.id).finally(function() {
            fetchToc().catch(showError);
        });
    }

    // Set SCORM start time.
    function setStartTime(scoId) {
        var tracks = [{
            element: 'x.start.time',
            value: $mmUtil.timestamp()
        }];
        return $mmaModScorm.saveTracks(scoId, attempt, tracks, offline, scorm).then(function() {
            if (!offline) {
                // New online attempt created, update cached data about online attempts.
                $mmaModScorm.getAttemptCount(scorm.id, undefined, false, true);
            }
        });
    }

    $scope.showToc = $mmaModScorm.displayTocInPlayer(scorm);
    if ($scope.showToc) {
        // Setup TOC popover.
        $ionicPopover.fromTemplateUrl('addons/mod_scorm/templates/toc.html', {
            scope: $scope,
        }).then(function(popover) {
            $scope.popover = popover;
        });
    }

    // Fetch the SCORM data.
    fetchData().then(function() {
        if (currentSco) {
            // Set start time if it's a new attempt.
            var promise = newAttempt ? setStartTime(currentSco.id) : $q.when();
            return promise.catch(showError).finally(function() {
                // Load SCO.
                loadSco(currentSco);
            });
        }
    }).finally(function() {
        $scope.loaded = true;
    });

    $scope.loadSco = function(sco) {
        if (!sco.prereq || !sco.isvisible || !sco.launch) {
            return;
        }

        $scope.popover.hide();
        loadSco(sco);
    };

    // Listen for events to update the TOC and navigate through SCOs.
    var tocObserver = $mmEvents.on(mmaModScormEventUpdateToc, function(data) {
        if (data.scormid === scorm.id) {
            if (offline) {
                // Wait a bit to make sure data is stored.
                $timeout(refreshToc, 100);
            } else {
                refreshToc();
            }
        }
    });

    var launchNextObserver = $mmEvents.on(mmaModScormEventLaunchNextSco, function(data) {
        if (data.scormid === scorm.id && $scope.nextSco) {
            loadSco($scope.nextSco);
        }
    });

    var launchPrevObserver = $mmEvents.on(mmaModScormEventLaunchPrevSco, function(data) {
        if (data.scormid === scorm.id && $scope.previousSco) {
            loadSco($scope.previousSco);
        }
    });

    var goOfflineObserver = $mmEvents.on(mmaModScormEventGoOffline, function(data) {
        if (data.scormid === scorm.id && !offline) {
            offline = true;
            $timeout(function() {
                // Wait a bit to prevent collisions between this store and SCORM API's store.
                $mmaModScormHelper.convertAttemptToOffline(scorm, attempt).catch(showError).finally(function() {
                    refreshToc();
                });
            }, 500);
        }
    });

    $scope.$on('$destroy', function() {
        tocObserver && tocObserver.off && tocObserver.off();
        launchNextObserver && launchNextObserver.off && launchNextObserver.off();
        launchPrevObserver && launchPrevObserver.off && launchPrevObserver.off();
        goOfflineObserver && goOfflineObserver.off && goOfflineObserver.off();
    });
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

const renderPreview = function renderPreview() {
  return (

preview

  );
};

const renderEditor = function renderEditor() {
  return (

Title:

<input />

        <textarea />

  );
};

class BlogEditor extends Component {
  static propTypes = {
    content: PropTypes.object,
    onSubmit: PropTypes.func.isRequired,
  }

  static defaultProps = {
    content: {},
  }

  constructor(props) {
    super(props);
    this.state = {
      isPreview: false,
      content: props.content,
    };
  }

  render() {
    return (

          {
            this.state.isPreview ? renderPreview(this.state.content)
            : renderEditor(this.state.content)
          }

            <button onClick={() => this.setState({ isPreview: !this.state.isPreview })}>
              { this.state.isPreview ? 'Edit' : 'Preview' }
            </button>
            <button onClick={() => this.props.onSubmit(this.state.content)}>Submit</button>

    );
  }
}

export default BlogEditor;
<commit_msg>enable blog editor to preview markdown<commit_after>import React, { Component, PropTypes } from 'react';
import ReactMarkdown from 'react-markdown';

class BlogEditor extends Component {
  static propTypes = {
    blog: PropTypes.object,
    onSubmit: PropTypes.func.isRequired,
  }

  static defaultProps = {
    blog: {
      title: '',
      content: '',
    },
  }

  constructor(props) {
    super(props);
    const blog = props.blog;

    this.state = {
      isPreview: false,
      title: blog.title,
      blog: blog.blog,
    };

    this.updateTitle = this.updateTitle.bind(this);
    this.updateContent = this.updateContent.bind(this);
  }

  updateContent(evt) {
    this.setState({
      content: evt.target.value,
    });
  }

  updateTitle(evt) {
    this.setState({
      title: evt.target.value,
    });
  }

  renderPreview() {
    return (

{this.state.title}

        <ReactMarkdown source={this.state.content} />

    );
  }

  renderEditor() {
    return (

Title:

            <input onChange={this.updateTitle} value={this.state.title} />

          <textarea onChange={this.updateContent} value={this.state.content} />

    );
  }

  render() {
    return (

          {
            this.state.isPreview ? this.renderPreview()
            : this.renderEditor(this.state.content)
          }

            <button onClick={() => this.setState({ isPreview: !this.state.isPreview })}>
              { this.state.isPreview ? 'Edit' : 'Preview' }
            </button>
            <button onClick={() => this.props.onSubmit(this.state.content)}>Submit</button>

    );
  }
}

export default BlogEditor;
<|endoftext|>


<commit_before>/*jslint indent: 4, plusplus: true */
/*global YUI, window */

(function () {
    'use strict';

    //-- PipelineEvents -------------------------------------------------------

    function PipelineEvents() {
        this.events = {};
    }

    PipelineEvents.prototype = {

        /**
         * Invokes the subscribers that are interested in the specified action
         * on the given target.
         *
         * @param {String} target The task id of the target (e.g., "searchbox")
         *
         * @param {String} targetAction The action that is being reported (e.g., "beforeFlush")
         *
         * @param {Function} done A function which accepts as single parameter
         *      the number of subscribers that have been invoked.
         */
        fire: function (target, targetAction, done) {
            var subscribedActions = this.events[target] && this.events[target][targetAction],
                numSubscribedActions = subscribedActions && subscribedActions.length,
                i = 0,
                eventsCompleted = 0,
                event = {
                    target: target,
                    targetAction: targetAction
                },
                eventDone = function () {
                    if (done && ++eventsCompleted === numSubscribedActions) {
                        done(eventsCompleted);
                    }
                },
                actionArguments = [
                    event,
                    eventDone
                ],
                callback = function (event, done) {
                    return done();
                };

            if (subscribedActions) {
                // add optional arguments to the arguments passed to the action
                Array.prototype.push.apply(actionArguments, Array.prototype.slice.call(arguments, 0).slice(3));
                while (i < subscribedActions.length) {
                    if (subscribedActions[i].unsubscribed) {
                        // neutralized unsubscribed action but still execute the callback to fire
                        subscribedActions[i] = callback;
                    }
                    subscribedActions[i++].apply(this, actionArguments);
                }
            } else if (done) {
                done(0);
            }
        },

        /**
         * Subscribes to a list of actions on multiple targets.
         *
         * @param {Object} targets The targets and the corresponding actions
         *      on those targets we want to subscribe to, e.g.
         *          {
         *              "searchbox": ['beforeRender', 'afterFlush'],
         *              "footer": ['beforeDisplay']
         *          }
         *
         * @param {Function} subscribedAction a callback function which accepts
         *      the following parameters:
         *          - event: an object containg the following properties:
         *              - target (e.g., "searchbox")
         *              - targetAction (e.g., "beforeRender")
         *          - done: a calback which must be invoked once the subscriber
         *                  has finished executing. This allows a subscriber to
         *                  be either synchronous or asynchronous and guarantee
         *                  that subscribers are invoked in the right order.
         *
         * @return {PipelineEvents.Subscription} a subscription object
         */
        subscribe: function (targets, subscribedAction) {
            var i, target, targetAction;

            for (target in targets) {
                if (targets.hasOwnProperty(target)) {
                    if (!this.events[target]) {
                        this.events[target] = {};
                    }
                    for (i = 0; i < targets[target].length; i++) {
                        targetAction = targets[target][i];
                        if (!this.events[target][targetAction]) {
                            this.events[target][targetAction] = [];
                        }
                        this.events[target][targetAction].push(subscribedAction);
                    }
                }
            }

            return new PipelineEvents.Subscription(targets, subscribedAction);
        },

        once: function (targets, subscribedAction) {
            var subscription = this.subscribe(targets, function () {
                subscribedAction.apply(this, arguments);
                subscription.unsubscribe();
            });
        }
    };

    //-- PipelineEvents.Subscription ------------------------------------------

    PipelineEvents.Subscription = function (targets, subscribedAction) {
        this.targets = targets;
        this.subscribedAction = subscribedAction;
    };

    PipelineEvents.Subscription.prototype = {

        unsubscribe: function () {
            this.subscribedAction.unsubscribed = true;
            // Removing the subscribed action can cause issues if event actions
            // are being called and the array size changes in the middle, so we
            // instead mark as unsubscribed
        }
    };

    //-------------------------------------------------------------------------
    // Make this library available either as a YUI module, if YUI is defined,
    // which is the case on the server, or as an object attached to the window
    // object on the client side.

    if (typeof YUI !== 'undefined') {

        YUI.add('target-action-events', function (Y) {
            Y.namespace('Pipeline').Events = PipelineEvents;
        });

    } else if (typeof window !== 'undefined') {

        if (!window.Pipeline) {
            window.Pipeline = {};
        }

        window.Pipeline.Events = PipelineEvents;
    }

}());
<commit_msg>[bug 6493137] Light refactoring and documentation<commit_after>/*jslint indent: 4, plusplus: true */
/*global YUI, window */

(function () {
    'use strict';

    function ASYNC_NOOP(event, callback) {
        callback();
    }

    /**
     * @class PipelineEvents
     * @constructor
     */
    function PipelineEvents() {
        this.events = {};
    }

    PipelineEvents.prototype = {

        /**
         * Invokes the subscribers that are interested in the specified action
         * on the given target.
         *
         * @method fire
         * @param {String} target The task that generated this event (e.g., "searchbox")
         * @param {String} action The action that is being reported (e.g., "beforeFlush")
         * @param {Function} callback A function which accepts as single
         *      parameter the number of subscribers that have been invoked
         *      as a result of this event being fired...
         */
        fire: function (target, action, callback) {
            var subscriber,
                subscribers,
                subscribersCount,
                subscribersInvoked,
                evt,
                fn,
                args,
                i;

            subscribers = this.events[target] && this.events[target][action];

            if (!subscribers) {
                if (callback) {
                    callback(0);
                }
                return;
            }

            subscribersInvoked = 0;
            subscribersCount = subscribers.length;

            // The event object passed to the subscribers.
            evt = {
                target: target,
                action: action
            };

            // Subscribers can be asynchronous! Once they are done executing,
            // they have to call a function which is passed as their second
            // argument. This function checks whether all the subscribers have
            // been invoked. If so, it invokes the callback passed to the
            // 'fire' method...
            fn = function () {
                if (callback && ++subscribersInvoked === subscribersCount) {
                    callback(subscribersInvoked);
                }
            };

            // The arguments passed to a subscriber.
            args = [evt, fn].concat(Array.prototype.slice.call(arguments, 0).slice(3));

            for (i = 0; i < subscribersCount; i++) {
                subscriber = subscribers[i];

                if (subscriber.unsubscribed) {
                    // This subscriber is not interested in subscribing to that
                    // event, so we replace it with a 'noop' function...
                    subscriber = ASYNC_NOOP;
                }

                subscriber.apply(this, args);
            }
        },

        /**
         * Subscribes to a list of actions on multiple targets.
         *
         * @method subscribe
         * @param {Object} targets The targets and the corresponding actions
         *      on those targets we want to subscribe to, e.g.
         *          {
         *              "searchbox": ["beforeRender", "afterFlush"],
         *              "footer": ["beforeDisplay"]
         *          }
         * @param {Function} fn callback function with the following parameters:
         *          - event: an object containg the following properties:
         *              - target (e.g., "searchbox")
         *              - action (e.g., "beforeRender")
         *          - callback: a function which must be invoked once the subscriber
         *              has finished executing. This allows a subscriber to be either
         *              synchronous or asynchronous while guaranteeing that we can
         *              resume processing once all the subscribers have been invoked.
         * @return {PipelineEvents.Subscription} a subscription object
         */
        subscribe: function (targets, fn) {
            var target,
                actions,
                action,
                subscribers,
                i;

            for (target in targets) {
                if (targets.hasOwnProperty(target)) {

                    if (!this.events[target]) {
                        this.events[target] = {};
                    }

                    actions = targets[target];

                    for (i = 0; i < actions.length; i++) {
                        action = actions[i];

                        if (!this.events[target][action]) {
                            this.events[target][action] = [];
                        }

                        subscribers = this.events[target][action];
                        subscribers.push(fn);
                    }
                }
            }

            return new PipelineEvents.Subscription(fn);
        },

        /**
         * Subscribes to a list of actions on multiple targets. The supplied
         * callback will only be executed once, even though we might have
         * subscribed to multiple targets and actions...
         *
         * @method once
         * @return {PipelineEvents.Subscription} a subscription object
         */
        once: function (targets, fn) {
            var subscription = this.subscribe(targets, function () {
                fn.apply(this, arguments);
                subscription.unsubscribe();
            });

            return subscription;
        }
    };

    /**
     * @class PipelineEvents.Subscription
     * @constructor
     */
    PipelineEvents.Subscription = function (fn) {
        this.fn = fn;
    };

    PipelineEvents.Subscription.prototype = {

        /**
         * Prevents a subscriber function from being called more than once.
         * @method unsubscribe
         * @see PipelineEvents::subscribe
         */
        unsubscribe: function () {
            // Removing the subscribed action can cause issues if event actions
            // are being called and the array size changes in the middle, so we
            // instead mark as unsubscribed.
            this.fn.unsubscribed = true;
        }
    };

    // Make this library available either as a YUI module, if YUI is defined,
    // which is the case on the server, or as an object attached to the window
    // object on the client side.

    if (typeof YUI !== 'undefined') {

        YUI.add('target-action-events', function (Y) {
            Y.namespace('Pipeline').Events = PipelineEvents;
        });

    } else if (typeof window !== 'undefined') {

        if (!window.Pipeline) {
            window.Pipeline = {};
        }

        window.Pipeline.Events = PipelineEvents;
    }

}());
<|endoftext|>


<commit_before><commit_msg>8.3 argumentos e parametros de funcao<commit_after><|endoftext|>


<commit_before>X.actionLoaded(function(event, fn, params) {
   jQuery('.image_container').gallerizer({
    loading_text: _('Loading images'),             // if set, text will display while images are loading
        loading_image: 'http://i302.photobucket.com/albums/nn92/wandoledzep/spinner.gif',
                                     // image to display while images are loading if no text is set
        wrapper_class: 'thumbs',     // classname for outer wrapping div
        full_image_link: true,
        full_image_link_label: "Ver imagen completa",
        information: [
                {attribute: "title", label: _("Name")},
                {attribute: "data-nodeid", label: _("Node ID")},
                {attribute: "data-mime", label: _("Type")},
                {attribute: "data-dimensions", label: _("Dimensions")},
                {attribute: "data-size", label: _("Size")}

        ],
        // Open image in new window
        full_image_link_callback: function(img) {
                var url = $(img).attr("data-original_path");
                window.open(url, "imageViewer");
        }
    });
});
<commit_msg>Context aware image gallery initialization<commit_after>X.actionLoaded(function(event, fn, params) {
   fn('.image_container').gallerizer({
    loading_text: _('Loading images'),             // if set, text will display while images are loading
        loading_image: 'http://i302.photobucket.com/albums/nn92/wandoledzep/spinner.gif',
                                     // image to display while images are loading if no text is set
        wrapper_class: 'thumbs',     // classname for outer wrapping div
        full_image_link: true,
        full_image_link_label: "Ver imagen completa",
        information: [
                {attribute: "title", label: _("Name")},
                {attribute: "data-nodeid", label: _("Node ID")},
                {attribute: "data-mime", label: _("Type")},
                {attribute: "data-dimensions", label: _("Dimensions")},
                {attribute: "data-size", label: _("Size")}

        ],
        // Open image in new window
        full_image_link_callback: function(img) {
                var url = $(img).attr("data-original_path");
                window.open(url, "imageViewer");
        }
    });
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from './template';

let   FabricPropertyParsers = {
  asInt: (x) => {
    return parseInt(x, 10);
  },
  asFloat: (x) => {
    return parseFloat(x, 10);
  }
};

let Selection = Ember.Object.extend ({
  selection: null,
  changeHandler: null,
  unknownProperty(key) {
    let selection = this.get('selection');
    if (Ember.isEmpty(selection)) {
      return '';
    }
    let selectionType = selection.get('type');
    if (selectionType === 'group') {
      let values = Ember.A(selection.objects).mapBy(key);
      let uniqueValues = Ember.A(values).uniq();
      if (uniqueValues.length === 1) {
        return uniqueValues[0] || '';
      } else {
        return '';
      }
    } else {
      return Ember.get(selection, key);
    }
  },

  setUnknownProperty(keyWithParser, value) {
    let selection = this.get('selection');
    if (Ember.isEmpty(selection)) {
      return '';
    }
    let selectionType = selection.get('type');
    let objects = (selectionType === 'group') ? selection.objects : [selection];
    let [key, parsedValue] = this.parsedValueForKey(keyWithParser, value);
    Ember.A(objects).forEach(function(obj) {
      Ember.set(obj, key, parsedValue);
    });
    let changeHandler = this.get('changeHandler');
    if(changeHandler) {
      changeHandler();
    }
    return parsedValue;
  },

  parsedValueForKey(key, value) {
    let keyParts = key.split('-');
    console.log('keyParts: ', keyParts);
    if ( keyParts.length < 2 ) {
      return [key, value];
    }
    let parser = FabricPropertyParsers[keyParts[keyParts.length-1]];
    if (  parser ) {
      keyParts.pop();
      console.log('now keyParts: ', keyParts);
      return [keyParts.join('.'), parser(value)];
    } else {
      return [key, value];
    }
  }
});

export default Ember.Component.extend({
  layout,
  currentCanvas: null,
  classNames: ['embric-editor'],
  currentCanvasJSON() {
    let canvas = this.get('currentCanvas.fabricCanvas');
    let json = JSON.stringify(canvas);
    return json;
  },
  redrawDebounce: 100,

  selection: null,
  _updateSelection() {
    let canvas = this.get('currentCanvas.fabricCanvas');
    let selection = canvas.getActiveGroup() || canvas.getActiveObject();
    this.set('selection', Selection.create({selection, changeHandler: () =>{
      Ember.run.debounce(this, '_redraw', this.get('redrawDebounce'), false);
    }}));
    canvas.renderAll();
  },
  _redraw() {
    let canvas = this.get('currentCanvas.fabricCanvas');
    canvas.renderAll();
  },

  actions: {
    registerCanvas(fabricCanvas) {
      this.set('currentCanvas', fabricCanvas);
      let canvas = fabricCanvas.get('fabricCanvas');
      let updateSelection = () => {
        this._updateSelection();
      };
      canvas
        .on('object:selected', updateSelection)
        .on('group:selected', updateSelection)
        .on('path:created', updateSelection)
        .on('selection:cleared', updateSelection);
      window.cv = fabricCanvas.fabricCanvas;
    },
    setJSON(json) {
      let canvas = this.get('currentCanvas.fabricCanvas');
      canvas.loadFromJSON(json, () => {
        canvas.renderAll();
      });
    },

  }
});
<commit_msg>fix lint/jcss issues<commit_after>import Ember from 'ember';
import layout from './template';

let   FabricPropertyParsers = {
  asInt: (x) => {
    return parseInt(x, 10);
  },
  asFloat: (x) => {
    return parseFloat(x, 10);
  }
};

let Selection = Ember.Object.extend({
  selection: null,
  changeHandler: null,
  unknownProperty(key) {
    let selection = this.get('selection');
    if (Ember.isEmpty(selection)) {
      return '';
    }
    let selectionType = selection.get('type');
    if (selectionType === 'group') {
      let values = Ember.A(selection.objects).mapBy(key);
      let uniqueValues = Ember.A(values).uniq();
      if (uniqueValues.length === 1) {
        return uniqueValues[0] || '';
      } else {
        return '';
      }
    } else {
      return Ember.get(selection, key);
    }
  },

  setUnknownProperty(keyWithParser, value) {
    let selection = this.get('selection');
    if (Ember.isEmpty(selection)) {
      return '';
    }
    let selectionType = selection.get('type');
    let objects = (selectionType === 'group') ? selection.objects : [selection];
    let [key, parsedValue] = this.parsedValueForKey(keyWithParser, value);
    Ember.A(objects).forEach(function(obj) {
      Ember.set(obj, key, parsedValue);
    });
    let changeHandler = this.get('changeHandler');
    if (changeHandler) {
      changeHandler();
    }
    return parsedValue;
  },

  parsedValueForKey(key, value) {
    let keyParts = key.split('-');
    console.log('keyParts: ', keyParts);
    if (keyParts.length < 2) {
      return [key, value];
    }
    let parser = FabricPropertyParsers[keyParts[keyParts.length - 1]];
    if (parser) {
      keyParts.pop();
      console.log('now keyParts: ', keyParts);
      return [keyParts.join('.'), parser(value)];
    } else {
      return [key, value];
    }
  }
});

export default Ember.Component.extend({
  layout,
  currentCanvas: null,
  classNames: ['embric-editor'],
  currentCanvasJSON() {
    let canvas = this.get('currentCanvas.fabricCanvas');
    let json = JSON.stringify(canvas);
    return json;
  },
  redrawDebounce: 100,

  selection: null,
  _updateSelection() {
    let canvas = this.get('currentCanvas.fabricCanvas');
    let selection = canvas.getActiveGroup() || canvas.getActiveObject();
    this.set('selection', Selection.create({selection, changeHandler: () =>{
      Ember.run.debounce(this, '_redraw', this.get('redrawDebounce'), false);
    }}));
    canvas.renderAll();
  },
  _redraw() {
    let canvas = this.get('currentCanvas.fabricCanvas');
    canvas.renderAll();
  },

  actions: {
    registerCanvas(fabricCanvas) {
      this.set('currentCanvas', fabricCanvas);
      let canvas = fabricCanvas.get('fabricCanvas');
      let updateSelection = () => {
        this._updateSelection();
      };
      canvas
        .on('object:selected', updateSelection)
        .on('group:selected', updateSelection)
        .on('path:created', updateSelection)
        .on('selection:cleared', updateSelection);
      window.cv = fabricCanvas.fabricCanvas;
    },
    setJSON(json) {
      let canvas = this.get('currentCanvas.fabricCanvas');
      canvas.loadFromJSON(json, () => {
        canvas.renderAll();
      });
    }
  }
});
<|endoftext|>


<commit_before>import data from '../util/data';
import eventsApplier from './events';
import patchAttributeMethods from './patch-attribute-methods';
import propertiesInit from './properties-init';
import propertiesCreated from './properties-created';
import propertiesReady from './properties-ready';
import prototypeApplier from './prototype';
import resolve from './resolve';

// TODO Remove this when we no longer support the legacy definitions and only
// support a superset of a native property definition.
function ensurePropertyFunctions (opts) {
  let properties = opts.properties;
  let names = Object.keys(properties || {});
  return names.reduce(function (descriptors, descriptorName) {
    descriptors[descriptorName] = opts.properties[descriptorName];
    if (typeof descriptors[descriptorName] !== 'function') {
      descriptors[descriptorName] = propertiesInit(descriptors[descriptorName]);
    }
    return descriptors;
  }, {});
}

function ensurePropertyDefinitions (elem, propertyFunctions) {
  return Object.keys(propertyFunctions || {}).reduce(function (descriptors, descriptorName) {
    descriptors[descriptorName] = propertyFunctions[descriptorName](descriptorName);
    return descriptors;
  }, {});
}

export default function (opts) {
  const {
    attribute,
    created,
    events,
    isNative,
    properties,
    prototype,
    ready,
    render,
    resolvedAttribute
  } = opts;
  const applyEvents = eventsApplier(opts);
  const applyPrototype = prototypeApplier(opts);
  const propertyFunctions = ensurePropertyFunctions(opts);

  // Performance critical code!
  return function () {
    const info = data(this);
    const resolved = this.hasAttribute(resolvedAttribute);
    const propertyDefinitions = properties ? ensurePropertyDefinitions(this, propertyFunctions) : null;

    if (info.created) return;
    info.created = true;

    if (!isNative) {
      if (attribute) {
        patchAttributeMethods(this);
      }

      if (prototype) {
        applyPrototype(this);
      }
    }

    if (propertyDefinitions) {
      propertiesCreated(this, propertyDefinitions);
    }

    if (events) {
      applyEvents(this);
    }

    if (created) {
      created(this);
    }

    if (render && !resolved) {
      render(this);
    }

    if (propertyDefinitions) {
      propertiesReady(this, propertyDefinitions);
    }

    if (ready) {
      ready(this);
    }

    if (info.readyCallbacks) {
      info.readyCallbacks.forEach(cb => cb());
      info.readyCallbacks = null;
    }

    if (!resolved) {
      resolve(this, opts);
    }
  };
}
<commit_msg>Arguably cleaner logic.<commit_after>import data from '../util/data';
import eventsApplier from './events';
import patchAttributeMethods from './patch-attribute-methods';
import propertiesInit from './properties-init';
import propertiesCreated from './properties-created';
import propertiesReady from './properties-ready';
import prototypeApplier from './prototype';
import resolve from './resolve';

// TODO Remove this when we no longer support the legacy definitions and only
// support a superset of a native property definition.
function ensurePropertyFunctions (opts) {
  let properties = opts.properties;
  let names = Object.keys(properties || {});
  return names.reduce(function (descriptors, descriptorName) {
    descriptors[descriptorName] = opts.properties[descriptorName];
    if (typeof descriptors[descriptorName] !== 'function') {
      descriptors[descriptorName] = propertiesInit(descriptors[descriptorName]);
    }
    return descriptors;
  }, {});
}

function ensurePropertyDefinitions (elem, propertyFunctions) {
  return Object.keys(propertyFunctions || {}).reduce(function (descriptors, descriptorName) {
    descriptors[descriptorName] = propertyFunctions[descriptorName](descriptorName);
    return descriptors;
  }, {});
}

export default function (opts) {
  const {
    attribute,
    created,
    events,
    isNative,
    properties,
    prototype,
    ready,
    render,
    resolvedAttribute
  } = opts;
  const applyEvents = eventsApplier(opts);
  const applyPrototype = prototypeApplier(opts);
  const propertyFunctions = ensurePropertyFunctions(opts);

  // Performance critical code!
  return function () {
    const info = data(this);
    const resolved = this.hasAttribute(resolvedAttribute);
    const propertyDefinitions = properties ? ensurePropertyDefinitions(this, propertyFunctions) : null;
    const readyCallbacks = info.readyCallbacks;

    if (info.created) {
      return;
    }

    info.created = true;

    if (!isNative) {
      if (attribute) {
        patchAttributeMethods(this);
      }

      if (prototype) {
        applyPrototype(this);
      }
    }

    if (propertyDefinitions) {
      propertiesCreated(this, propertyDefinitions);
    }

    if (events) {
      applyEvents(this);
    }

    if (created) {
      created(this);
    }

    if (render && !resolved) {
      render(this);
    }

    if (propertyDefinitions) {
      propertiesReady(this, propertyDefinitions);
    }

    if (ready) {
      ready(this);
    }

    if (readyCallbacks) {
      readyCallbacks.forEach(cb => cb());
      info.readyCallbacks = null;
    }

    if (!resolved) {
      resolve(this, opts);
    }
  };
}
<|endoftext|>


<commit_before>export default {
  el: {
    colorpicker: {
      confirm: 'Qabul qilish',
      clear: 'Tozalash'
    },
    datepicker: {
      now: 'Hozir',
      today: 'Bugun',
      cancel: 'Bekor qilish',
      clear: 'Tozalash',
      confirm: 'Qabul qilish',
      selectDate: 'Kunni taladingiz',
      selectTime: 'Soatni tanladingiz',
      startDate: 'Boshlanish sanasi',
      startTime: 'Boshlanish vaqti',
      endDate: 'Tugash sanasi',
      endTime: 'Tugash vaqti',
      prevYear: 'O`tgan yil',
      nextYear: 'Kelgusi yil',
      prevMonth: 'O`tgan oy',
      nextMonth: 'Kelgusi oy',
      year: 'Yil',
      month1: 'Yanvar',
      month2: 'Fevral',
      month3: 'Mart',
      month4: 'Aprel',
      month5: 'May',
      month6: 'Iyun',
      month7: 'Iyul',
      month8: 'Avgust',
      month9: 'Sentyabr',
      month10: 'Oktabr',
      month11: 'Noyabr',
      month12: 'Dekabr',
      week: 'Hafta',
      weeks: {
        sun: 'Yak',
        mon: 'Dush',
        tue: 'Sesh',
        wed: 'Chor',
        thu: 'Pay',
        fri: 'Jum',
        sat: 'Shan'
      },
      months: {
        jan: 'Yan',
        feb: 'Fev',
        mar: 'Mar',
        apr: 'Apr',
        may: 'May',
        jun: 'Iyun',
        jul: 'Iyul',
        aug: 'Avg',
        sep: 'Sen',
        oct: 'Okt',
        nov: 'Noy',
        dec: 'Dek'
      }
    },
    select: {
      loading: 'Yuklanmoqda',
      noMatch: 'Mos ma`lumot yo`q',
      noData: 'Ma`lumot yo`q',
      placeholder: 'Tanladizngiz'
    },
    cascader: {
      noMatch: 'Mos ma`lumot yo`q',
      loading: 'Yuklanmoqda',
      placeholder: 'Tanladingiz',
      noData: 'Ma`lumot yo`q'
    },
    pagination: {
      goto: 'Borish',
      pagesize: '/page',
      total: 'Jami {total}',
      pageClassifier: ''
    },
    messagebox: {
      title: 'Xabar',
      confirm: 'Qabul qilish',
      cancel: 'Bekor qilish',
      error: 'Xatolik'
    },
    upload: {
      deleteTip: 'O`chirish tugmasini bosib o`chiring',
      delete: 'O`chirish',
      preview: 'Oldin ko`rish',
      continue: 'Joylashtirish'
    },
    table: {
      emptyText: 'Bo`sh',
      confirmFilter: 'Qabul qilish',
      resetFilter: 'Oldingi holatga qaytarish',
      clearFilter: 'Jami',
      sumText: 'Summasi'
    },
    tree: {
      emptyText: 'Ma`lumot yo`q'
    },
    transfer: {
      noMatch: 'Mos ma`lumot topilmadi',
      noData: 'Ma`lumot yo`q',
      titles: ['List 1', 'List 2'],
      filterPlaceholder: 'Kalit so`zni kiriting',
      noCheckedFormat: '{total} ta element',
      hasCheckedFormat: '{checked}/{total} belgilandi'
    },
    image: {
      error: 'Xatolik'
    },
    pageHeader: {
      title: 'Orqaga'
    }
  }
};
<commit_msg>I18n: Corrected translations of Uzbek language (#16226)<commit_after>export default {
  el: {
    colorpicker: {
      confirm: 'Qabul qilish',
      clear: 'Tozalash'
    },
    datepicker: {
      now: 'Hozir',
      today: 'Bugun',
      cancel: 'Bekor qilish',
      clear: 'Tozalash',
      confirm: 'Qabul qilish',
      selectDate: 'Kunni tanlash',
      selectTime: 'Soatni tanlash',
      startDate: 'Boshlanish sanasi',
      startTime: 'Boshlanish vaqti',
      endDate: 'Tugash sanasi',
      endTime: 'Tugash vaqti',
      prevYear: 'Oʻtgan yil',
      nextYear: 'Kelgusi yil',
      prevMonth: 'Oʻtgan oy',
      nextMonth: 'Kelgusi oy',
      year: 'Yil',
      month1: 'Yanvar',
      month2: 'Fevral',
      month3: 'Mart',
      month4: 'Aprel',
      month5: 'May',
      month6: 'Iyun',
      month7: 'Iyul',
      month8: 'Avgust',
      month9: 'Sentabr',
      month10: 'Oktabr',
      month11: 'Noyabr',
      month12: 'Dekabr',
      week: 'Hafta',
      weeks: {
        sun: 'Yak',
        mon: 'Dush',
        tue: 'Sesh',
        wed: 'Chor',
        thu: 'Pay',
        fri: 'Jum',
        sat: 'Shan'
      },
      months: {
        jan: 'Yan',
        feb: 'Fev',
        mar: 'Mar',
        apr: 'Apr',
        may: 'May',
        jun: 'Iyun',
        jul: 'Iyul',
        aug: 'Avg',
        sep: 'Sen',
        oct: 'Okt',
        nov: 'Noy',
        dec: 'Dek'
      }
    },
    select: {
      loading: 'Yuklanmoqda',
      noMatch: 'Mos maʼlumot yoʻq',
      noData: 'Maʼlumot yoʻq',
      placeholder: 'Tanladizngiz'
    },
    cascader: {
      noMatch: 'Mos maʼlumot topilmadi',
      loading: 'Yuklanmoqda',
      placeholder: 'Tanlash',
      noData: 'Maʼlumot yoʻq'
    },
    pagination: {
      goto: 'Oʻtish',
      pagesize: '/sahifa',
      total: 'Barchasi {total} ta',
      pageClassifier: ''
    },
    messagebox: {
      title: 'Xabar',
      confirm: 'Qabul qilish',
      cancel: 'Bekor qilish',
      error: 'Xatolik'
    },
    upload: {
      deleteTip: 'Oʻchirish tugmasini bosib oʻchiring',
      delete: 'Oʻchirish',
      preview: 'Oldin koʻrish',
      continue: 'Davom qilish'
    },
    table: {
      emptyText: 'Boʻsh',
      confirmFilter: 'Qabul qilish',
      resetFilter: 'Oldingi holatga qaytarish',
      clearFilter: 'Jami',
      sumText: 'Summasi'
    },
    tree: {
      emptyText: 'Maʼlumot yoʻq'
    },
    transfer: {
      noMatch: 'Mos maʼlumot topilmadi',
      noData: 'Maʼlumot yoʻq',
      titles: ['1-jadval', '2-jadval'],
      filterPlaceholder: 'Kalit soʻzni kiriting',
      noCheckedFormat: '{total} ta element',
      hasCheckedFormat: '{checked}/{total} ta belgilandi'
    },
    image: {
      error: 'Xatolik'
    },
    pageHeader: {
      title: 'Orqaga'
    }
  }
};
<|endoftext|>


<commit_before>/*globals ChangeTrigger,LocalSettings,SliderGroupCollection,Tab*/

var DatasetInterface = (function () {
    'use strict';

    function addClickListenerToToggles() {
        $("input[type='checkbox']")
            .off("click")
            .on("click", function (e) {
                $(e.target)
                    .parent()
                    .find('.toggle-group')
                    .toggleClass('active', $(e.target).is(':checked'));

                DatasetInterface.ChangeTrigger.trigger(e.target);
            });
    }

    function addChangeListenerToInputs() {
        $("div.input span.val input[type='text']")
            .off("change.convert")
            .on("change.convert", function (e) {
                window.DatasetInterface.ChangeTrigger.trigger(e.target);

                $(this).next("[type='hidden']").val(
                    Converter.reverseConvert.call(this, $(this).val())
                );
            });
    }

    /* Private: addChangeListenerToDatasetSwitch
     *
     * Add change listener to select#datasets. This select box is there to
     * switch between dataset versions.
     */
    function addChangeListenerToDatasetSwitch() {
        $('.switch-dataset select#datasets')
            .off('change')
            .on('change', function (e) {
                window.DatasetInterface.open($(this).val());
            });
    }

    function addClickListenerToHistory() {
        $("div.input span.history").off("click").on("click", function () {
            $("div.history." + $(this).data('key')).toggleClass("hidden");
        });
    }

    function addClickListenerToValidateButton() {
        $(".apply button[type=submit]").off("click").on("click", function () {
            $(this).find('img').show();
        });
    }

    function addClickToBackButton() {
        $(".overview .buttons button.back")
            .off("click").on("click", function (e) {
                e.preventDefault();

                $(".content.tab#overview").hide();
                $(".content.tab#input").show();
            });
    }

    function addClickListenerToDownloadDataset() {
        var button = $("button.button.download-dataset");

        button.off('click').on('click', function () {
            button.prop('disabled', true);

            $("form.download-dataset").submit();
        });
    }

    // Click listener to clone a dataset
    function addClickListenerToCloneDataset() {
        var button = $("button.button.clone-dataset");

        button.off('click').on('click', function () {
            button.prop('disabled', true);

            $("form.clone-dataset").submit();
        });
    }

    // This method set the default 'scaled NL' values for
    // attributes which were otherwise unknown.
    function setDefaultsForTab(tab) {
        var key,
            converted,
            displayInput,
            span,
            readOnly,
            defaultVal,
            defaults = this.defaults;

        $.when(defaults.fetch()).done(function () {
            tab.find('.editable').each(function () {
                span         = $(this).find('span.val');
                displayInput = span.find('input.display_input');
                readOnly     = span.find('.read-only');
                defaultVal   = defaults.data[$(this).data('key')];
                converted    = Converter.convertRounded.call(span, defaultVal);

                if (defaultVal) {
                    if (displayInput.length > 0 && !displayInput.val()) {
                        displayInput.val(converted);
                    }

                    if (readOnly.length > 0) {
                        readOnly.text(converted);
                    }
                }
            });
        });
    }

    function loadValues() {
        var value;

        $('input.value_input').each(function () {
            value = $(this).val();

            if (value) {
                $(this).prev(".display_input").val(
                    Converter.convertRounded.call(this, value)
                );
            }
        });
    }

    DatasetInterface.prototype = {
        enable: function () {
            this.tab = new Tab(
                $(".nav#input-nav"),
                function (group, tab) {
                    this.sliderGroupCollection.render(group);
                }.bind(this),
                this.localSettings
            ).enable();

            loadValues.call(this);
            addClickListenerToDownloadDataset.call(this);
            addClickListenerToCloneDataset.call(this);
            addChangeListenerToDatasetSwitch.call(this);
            addChangeListenerToInputs.call(this);
            addClickListenerToToggles.call(this);
            addClickListenerToValidateButton.call(this);
            addClickListenerToHistory.call(this);
        }
    }

    function DatasetInterface(datasetId) {
        this.datasetId             = datasetId;
        this.localSettings         = new LocalSettings(datasetId);
        this.sliderGroupCollection = new SliderGroupCollection(datasetId);
    }

    return {
        open: function (datasetId) {
            $.ajax({
                type: "GET",
                dataType: 'script',
                url: '/datasets/' + datasetId + '/edit.js'
            });
        },

        enable: function (datasetId) {
            var datasetInterface = new DatasetInterface(datasetId);

            datasetInterface.enable();

            return datasetInterface;
        }
    };
}());
<commit_msg>Detect changes upon input<commit_after>/*globals ChangeTrigger,LocalSettings,SliderGroupCollection,Tab*/

var DatasetInterface = (function () {
    'use strict';

    function addClickListenerToToggles() {
        $("input[type='checkbox']")
            .off("click")
            .on("click", function (e) {
                $(e.target)
                    .parent()
                    .find('.toggle-group')
                    .toggleClass('active', $(e.target).is(':checked'));

                DatasetInterface.ChangeTrigger.trigger(e.target);
            });
    }

    function addChangeListenerToInputs() {
        $("div.input span.val input[type='text']")
            .off("input.convert, change.convert")
            .on("input.convert, change.convert", function (e) {
                window.DatasetInterface.ChangeTrigger.trigger(e.target);

                $(this).next("[type='hidden']").val(
                    Converter.reverseConvert.call(this, $(this).val())
                );
            });
    }

    /* Private: addChangeListenerToDatasetSwitch
     *
     * Add change listener to select#datasets. This select box is there to
     * switch between dataset versions.
     */
    function addChangeListenerToDatasetSwitch() {
        $('.switch-dataset select#datasets')
            .off('change')
            .on('change', function (e) {
                window.DatasetInterface.open($(this).val());
            });
    }

    function addClickListenerToHistory() {
        $("div.input span.history").off("click").on("click", function () {
            $("div.history." + $(this).data('key')).toggleClass("hidden");
        });
    }

    function addClickListenerToValidateButton() {
        $(".apply button[type=submit]").off("click").on("click", function () {
            $(this).find('img').show();
        });
    }

    function addClickToBackButton() {
        $(".overview .buttons button.back")
            .off("click").on("click", function (e) {
                e.preventDefault();

                $(".content.tab#overview").hide();
                $(".content.tab#input").show();
            });
    }

    function addClickListenerToDownloadDataset() {
        var button = $("button.button.download-dataset");

        button.off('click').on('click', function () {
            button.prop('disabled', true);

            $("form.download-dataset").submit();
        });
    }

    // Click listener to clone a dataset
    function addClickListenerToCloneDataset() {
        var button = $("button.button.clone-dataset");

        button.off('click').on('click', function () {
            button.prop('disabled', true);

            $("form.clone-dataset").submit();
        });
    }

    // This method set the default 'scaled NL' values for
    // attributes which were otherwise unknown.
    function setDefaultsForTab(tab) {
        var key,
            converted,
            displayInput,
            span,
            readOnly,
            defaultVal,
            defaults = this.defaults;

        $.when(defaults.fetch()).done(function () {
            tab.find('.editable').each(function () {
                span         = $(this).find('span.val');
                displayInput = span.find('input.display_input');
                readOnly     = span.find('.read-only');
                defaultVal   = defaults.data[$(this).data('key')];
                converted    = Converter.convertRounded.call(span, defaultVal);

                if (defaultVal) {
                    if (displayInput.length > 0 && !displayInput.val()) {
                        displayInput.val(converted);
                    }

                    if (readOnly.length > 0) {
                        readOnly.text(converted);
                    }
                }
            });
        });
    }

    function loadValues() {
        var value;

        $('input.value_input').each(function () {
            value = $(this).val();

            if (value) {
                $(this).prev(".display_input").val(
                    Converter.convertRounded.call(this, value)
                );
            }
        });
    }

    DatasetInterface.prototype = {
        enable: function () {
            this.tab = new Tab(
                $(".nav#input-nav"),
                function (group, tab) {
                    this.sliderGroupCollection.render(group);
                }.bind(this),
                this.localSettings
            ).enable();

            loadValues.call(this);
            addClickListenerToDownloadDataset.call(this);
            addClickListenerToCloneDataset.call(this);
            addChangeListenerToDatasetSwitch.call(this);
            addChangeListenerToInputs.call(this);
            addClickListenerToToggles.call(this);
            addClickListenerToValidateButton.call(this);
            addClickListenerToHistory.call(this);
        }
    }

    function DatasetInterface(datasetId) {
        this.datasetId             = datasetId;
        this.localSettings         = new LocalSettings(datasetId);
        this.sliderGroupCollection = new SliderGroupCollection(datasetId);
    }

    return {
        open: function (datasetId) {
            $.ajax({
                type: "GET",
                dataType: 'script',
                url: '/datasets/' + datasetId + '/edit.js'
            });
        },

        enable: function (datasetId) {
            var datasetInterface = new DatasetInterface(datasetId);

            datasetInterface.enable();

            return datasetInterface;
        }
    };
}());
<|endoftext|>


<commit_before>/* detect-duplicates: check to see if source metadata identifier has already been used */
$(document).ready(function(){
    $('.detect_duplicates').change(detect_duplicates);
});

function detect_duplicates() {
  var $me = $(this);
  if ( $me.val() != '' ) {
    var url = '/catalog?f[source_metadata_identifier_ssim][]=' + $me.val();
    $.ajax({ url: url + '&format=json' })
    .done(function(data) {
      if ( data.docs.length > 0 ) {
        $me.parent().append('
This ID is already in use: view records using this Source Metadata ID.
');
      } else {
        $('#duplicates').remove();
      }
    });
  }
}
<commit_msg>Suggest portion note for records with duplicate source metadata identifiers (#529)<commit_after>/* detect-duplicates: check to see if source metadata identifier has already been used */
$(document).ready(function(){
    $('.detect_duplicates').change(detect_duplicates);
});

function detect_duplicates() {
  var $me = $(this);
  if ( $me.val() != '' ) {
    var url = '/catalog?f[source_metadata_identifier_ssim][]=' + $me.val();
    $.ajax({ url: url + '&format=json' })
    .done(function(data) {
      if ( data.docs.length > 0 ) {
        $me.parent().append('
This ID is already in use: view records using this Source Metadata ID.  Please consider using the Portion Note field to help differentiate between objects with the same metadata.
');
      } else {
        $('#duplicates').remove();
      }
    });
  }
}
<|endoftext|>


<commit_before>export { ArrayInterleaver };

/**
 *
 */
class ArrayInterleaver {

  /**
   * Rearrange array elements by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * The fromArray and toArray can not be an overlapped array.
   *   - They could be different array. Or,
   *   - Thay could be the same array but fromArray[ fromIndex ] to fromArray[ fromIndex + ( 2 * elementCountHalf ) - 1 ] and
   *       toArray[ toIndex ] to toArray[ toIndex + ( 2 * elementCountHalf ) - 1 ] can not overlapped.
   *
   * @param {Array} fromArray   The source array. It will not be modified. It is the source of copying.
   * @param {number} fromIndex  The copying will begin at fromArray[ fromIndex ].
   * @param {Array} toArray     The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number} toIndex    The writing will begin at toArray[ toIndex ].
   *
   * @param {number} elementCountHalf
   *   There will be ( 2 * elementCountHalf ) elements copied from fromArray[ fromIndex ] to toArray[ toIndex ] but in a re-arranged
   * interleaved order.
   *
   */
  static interleave_asGrouptTwo_from_to( fromArray, fromIndex, toArray, toIndex, elementCountHalf ) {
    for ( let i = 0, from = fromIndex, to = toIndex; i < elementCountHalf; ++i, ++from, to += 2 ) {
      toArray[ to     ] = fromArray[ from ];
      toArray[ to + 1 ] = fromArray[ from + elementCountHalf ];
    }
  }

  /**
   * Rearrange array elements so that it undo the .interleave_asGrouptTwo_from_to().
   *   - Only ( groupCount == 2 ) is supported.
   *
   * The fromArray and toArray can not be an overlapped array.
   *   - They could be different array. Or,
   *   - Thay could be the same array but fromArray[ fromIndex ] to fromArray[ fromIndex + ( 2 * elementCountHalf ) - 1 ] and
   *       toArray[ toIndex ] to toArray[ toIndex + ( 2 * elementCountHalf ) - 1 ] can not overlapped.
   *
   * @param {Array} fromArray   The source array. It will not be modified. It is the source of copying.
   * @param {number} fromIndex  The copying will begin at fromArray[ fromIndex ].
   * @param {Array} toArray     The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number} toIndex    The writing will begin at toArray[ toIndex ].
   *
   * @param {number} elementCountHalf
   *   There will be ( 2 * elementCountHalf ) elements copied from fromArray[ fromIndex ] to toArray[ toIndex ] but in a re-arranged
   * interleaved-undo order.
   *
   */
  static interleave_asGrouptTwo_from_to_undo( fromArray, fromIndex, toArray, toIndex, elementCountHalf ) {
    for ( let i = 0, from = fromIndex, to = toIndex; i < elementCountHalf; ++i, from += 2, ++to ) {
      toArray[ to ] = fromArray[ from ];
      toArray[ to + elementCountHalf ] = fromArray[ from + 1 ];
    }
  }

  /**
   * Rearrange array elements by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} arrayIn        The array to be re-arranged.
   * @param {number} indexBegin    Re-arrange the arrayIn from which element.
   * @param {number} elementCount  How many elements (begin at indexBegin) will be interleaved. It must be even (i.e. divisible by 2).
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements (from arrayIn) temporarily. Providing this array could reduce memory
   * re-allocation and improve performance.
   *
   * @return {Array}
   *   Retrun the (modified) arrayIn itself.
   */
  static interleave_asGrouptTwo_inPlace( arrayIn, indexBegin, elementCount, arrayTemp ) {

    if ( ( elementCount % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_byGrouptTwo(): `
        + `elementCount ( ${elementCount} ) must be even (i.e. divisible by 2).`
      );

    // Copy the elements to be re-arrange.
    arrayTemp.length = elementCount;
    for ( let i = 0; i < elementCount; ++i ) {
      arrayTemp[ i ] = arrayIn[ indexBegin + i ];
    }

    // Interleave the elements order.
    let elementCountHalf = Math.floor( elementCount / 2 );
    ArrayInterleaver.interleave_asGrouptTwo_from_to( arrayTemp, 0, arrayIn, indexBegin, elementCountHalf );
    return arrayIn;
  }

//!!! ...unfinished... (2022/07/12) interleave_asGrouptTwo_along_last_2nd_axis_from_to()

  /**
   * Rearrange array elements along height by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number} height    The virtual 2D array's height.
   * @param {number} width     The virtual 2D array's width.
   */
  static interleave_asGrouptTwo_alongHeight_from_to( fromArray, toArray, height, width ) {
    let elementCount = height * width;
    if ( elementCount != fromArray.length )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongHeight_from_to(): `
        + `( height * width ) = ( ${height} * ${width} ) = ${elementCount} `
        + ` should be the same as input array length ( ${fromArray.length} ).`
      );

    if ( ( height % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongHeight_from_to(): `
        + `height ( ${height} ) must be even (i.e. divisible by 2).`
      );

    toArray.length = elementCount;
    let elementCountHalf = Math.floor( elementCount / 2 );

    let fromIndex2, toIndex2;
    for ( let y = 0, fromIndex1 = 0, toIndex1 = 0; y < height; y += 2, fromIndex1 += width, toIndex1 = ( toIndex2 + width ) ) {
      fromIndex2 = fromIndex1 + elementCountHalf;
      toIndex2 = toIndex1 + width;
      for ( let i = 0; i < width; ++i ) {
        toArray[ toIndex1 + i ] = fromArray[ fromIndex1 + i ];
        toArray[ toIndex2 + i ] = fromArray[ fromIndex2 + i ];
      }
    }
  }

  /**
   * Rearrange array elements along height by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} arrayIn  The array to be re-arranged. It is an 1D array but represents a 2D array.
   *
   * @param {number} height  The virtual 2D array's height.
   * @param {number} width   The virtual 2D array's width.
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements (from arrayIn) temporarily. Providing this array could reduce memory
   * re-allocation and improve performance.
   *
   * @return {Array}
   *   Retrun the (modified) arrayIn itself.
   */
  static interleave_asGrouptTwo_alongHeight_inPlace( arrayIn, height, width, arrayTemp ) {

    arrayTemp.length = arrayIn.length;
    for ( let i = 0; i < arrayIn.length; ++i ) { // Copy the elements to be re-arrange.
      arrayTemp[ i ] = arrayIn[ i ];
    }

    ArrayInterleaver.interleave_asGrouptTwo_alongHeight_from_to( arrayTemp, arrayIn, height, width );
    return arrayIn;
  }

//!!! ...unfinished... (2022/07/12) interleave_asGrouptTwo_along_last_axis_from_to()
  /**
   * Rearrange array elements along the last axis by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {Function} pfnInterleaver  The function to do the destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number[]} shape   The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   */
  static interleave_asGrouptTwo_alongLastAxis_from_to( fromArray, toArray, ???, ...shape ) {
    if ( shape.length < 1 ) {
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to(): `
        + `shape.length ( ${shape.length} ) should be at least 1.`
      );
    }

    let elementCount = 1;
    for ( let i = 0; i < shape.length; ++i ) {
      elementCount *= shape[ i ];
    }

    if ( elementCount != fromArray.length ) {
      let shapeString = shape.join( " * " );
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to(): `
        + `shape ( ${shapeString} ) = ${elementCount} `
        + `should be the same as input array length ( ${fromArray.length} ).`
      );
    }

    let lastAxisId = shape.length - 1;
    let lastAxisLength = shape[ lastAxisId ];
    if ( ( lastAxisLength % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to(): `
        + `shape's last axis length ( ${lastAxisLength} ) must be even (i.e. divisible by 2).`
      );

    toArray.length = elementCount;
    let lastAxisLengthHalf = ( lastAxisLength / 2 );
    for ( let i = 0; i < elementCount; i += lastAxisLength ) {
      ArrayInterleaver.interleave_asGrouptTwo_from_to( fromArray, i, toArray, i, lastAxisLengthHalf );
    }
  }

  /**
   * Rearrange array elements along the last axis by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number[]} shape   The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   */
  static interleave_asGrouptTwo_alongLastAxis_from_to( fromArray, toArray, ...shape ) {
    if ( shape.length < 1 ) {
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to(): `
        + `shape.length ( ${shape.length} ) should be at least 1.`
      );
    }

    let elementCount = 1;
    for ( let i = 0; i < shape.length; ++i ) {
      elementCount *= shape[ i ];
    }

    if ( elementCount != fromArray.length ) {
      let shapeString = shape.join( " * " );
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to(): `
        + `shape ( ${shapeString} ) = ${elementCount} `
        + `should be the same as input array length ( ${fromArray.length} ).`
      );
    }

    let lastAxisId = shape.length - 1;
    let lastAxisLength = shape[ lastAxisId ];
    if ( ( lastAxisLength % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to(): `
        + `shape's last axis length ( ${lastAxisLength} ) must be even (i.e. divisible by 2).`
      );

    toArray.length = elementCount;
    let lastAxisLengthHalf = ( lastAxisLength / 2 );
    for ( let i = 0; i < elementCount; i += lastAxisLength ) {
      ArrayInterleaver.interleave_asGrouptTwo_from_to( fromArray, i, toArray, i, lastAxisLengthHalf );
    }
  }

  /**
   * Rearrange array elements along the last axis by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} arrayIn   The array to be re-arranged. It is an 1D array but represents a N-dimension array.
   * @param {number[]} shape  The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements (from arrayIn) temporarily. Providing this array could reduce memory
   * re-allocation and improve performance.
   *
   * @return {Array}
   *   Retrun the (modified) arrayIn itself.
   */
  static interleave_asGrouptTwo_alongLastAxis_inPlace( arrayIn, arrayTemp, ...shape ) {

    arrayTemp.length = arrayIn.length;
    for ( let i = 0; i < arrayIn.length; ++i ) { // Copy the elements to be re-arrange.
      arrayTemp[ i ] = arrayIn[ i ];
    }

    ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to( arrayTemp, arrayIn, ...shape );
    return arrayIn;
  }

//!!!
  /**
   * Rearrange array elements along the last axis so that it undo the .interleave_asGrouptTwo_alongLastAxis_from_to().
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number[]} shape   The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   */
//  static interleave_asGrouptTwo_alongWidth_from_to_undo( fromArray, toArray, height, width ) {
  static interleave_asGrouptTwo_alongLastAxis_from_to_undo( fromArray, toArray, ...shape ) {

    let elementCount = height * width;
    if ( elementCount != fromArray.length )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongWidth_from_to_undo(): `
        + `( height * width ) = ( ${height} * ${width} ) = ${elementCount} `
        + ` should be the same as input array length ( ${fromArray.length} ).`
      );

    if ( ( width % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongWidth_from_to_undo(): `
        + `width ( ${width} ) must be even (i.e. divisible by 2).`
      );

    toArray.length = elementCount;
    let widthHalf = ( width / 2 );
    for ( let i = 0; i < elementCount; i += width ) {
      ArrayInterleaver.interleave_asGrouptTwo_from_to_undo( fromArray, i, toArray, i, widthHalf );
    }
  }

  /**
   * Rearrange array elements along depth by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number} height    The virtual 2D array's height.
   * @param {number} width     The virtual 2D array's width.
   * @param {number} depth     The virtual 2D array's depth (i.e. channel count).
   */
  static interleave_asGrouptTwo_alongDepth_from_to( fromArray, toArray, height, width, depth ) {
    let elementCount = height * width * depth;
    if ( elementCount != fromArray.length )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongDepth_from_to(): `
        + `( height * width * depth ) = ( ${height} * ${width} * ${depth} ) = ${elementCount} `
        + ` should be the same as input array length ( ${fromArray.length} ).`
      );

    if ( ( depth % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongDepth_from_to(): `
        + `depth ( ${depth} ) must be even (i.e. divisible by 2).`
      );

    toArray.length = elementCount;
    let depthHalf = ( depth / 2 );
    for ( let i = 0; i < elementCount; i += depth ) {
      ArrayInterleaver.interleave_asGrouptTwo_from_to( fromArray, i, toArray, i, depthHalf );
    }
  }

}
<commit_msg>Update ArrayInterleaver.js<commit_after>export { ArrayInterleaver };

/**
 *
 */
class ArrayInterleaver {

  /**
   * Rearrange array elements by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * The fromArray and toArray can not be an overlapped array.
   *   - They could be different array. Or,
   *   - Thay could be the same array but fromArray[ fromIndex ] to fromArray[ fromIndex + ( 2 * elementCountHalf ) - 1 ] and
   *       toArray[ toIndex ] to toArray[ toIndex + ( 2 * elementCountHalf ) - 1 ] can not overlapped.
   *
   * @param {Array} fromArray   The source array. It will not be modified. It is the source of copying.
   * @param {number} fromIndex  The copying will begin at fromArray[ fromIndex ].
   * @param {Array} toArray     The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number} toIndex    The writing will begin at toArray[ toIndex ].
   *
   * @param {number} elementCountHalf
   *   There will be ( 2 * elementCountHalf ) elements copied from fromArray[ fromIndex ] to toArray[ toIndex ] but in a re-arranged
   * interleaved order.
   *
   */
  static interleave_asGrouptTwo_from_to( fromArray, fromIndex, toArray, toIndex, elementCountHalf ) {
    for ( let i = 0, from = fromIndex, to = toIndex; i < elementCountHalf; ++i, ++from, to += 2 ) {
      toArray[ to     ] = fromArray[ from ];
      toArray[ to + 1 ] = fromArray[ from + elementCountHalf ];
    }
  }

  /**
   * Rearrange array elements so that it undo the .interleave_asGrouptTwo_from_to().
   *   - Only ( groupCount == 2 ) is supported.
   *
   * The fromArray and toArray can not be an overlapped array.
   *   - They could be different array. Or,
   *   - Thay could be the same array but fromArray[ fromIndex ] to fromArray[ fromIndex + ( 2 * elementCountHalf ) - 1 ] and
   *       toArray[ toIndex ] to toArray[ toIndex + ( 2 * elementCountHalf ) - 1 ] can not overlapped.
   *
   * @param {Array} fromArray   The source array. It will not be modified. It is the source of copying.
   * @param {number} fromIndex  The copying will begin at fromArray[ fromIndex ].
   * @param {Array} toArray     The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number} toIndex    The writing will begin at toArray[ toIndex ].
   *
   * @param {number} elementCountHalf
   *   There will be ( 2 * elementCountHalf ) elements copied from fromArray[ fromIndex ] to toArray[ toIndex ] but in a re-arranged
   * interleaved-undo order.
   *
   */
  static interleave_asGrouptTwo_from_to_undo( fromArray, fromIndex, toArray, toIndex, elementCountHalf ) {
    for ( let i = 0, from = fromIndex, to = toIndex; i < elementCountHalf; ++i, from += 2, ++to ) {
      toArray[ to ] = fromArray[ from ];
      toArray[ to + elementCountHalf ] = fromArray[ from + 1 ];
    }
  }

  /**
   * Rearrange array elements by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} arrayIn        The array to be re-arranged.
   * @param {number} indexBegin    Re-arrange the arrayIn from which element.
   * @param {number} elementCount  How many elements (begin at indexBegin) will be interleaved. It must be even (i.e. divisible by 2).
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements (from arrayIn) temporarily. Providing this array could reduce memory
   * re-allocation and improve performance.
   *
   * @return {Array}
   *   Retrun the (modified) arrayIn itself.
   */
  static interleave_asGrouptTwo_inPlace( arrayIn, indexBegin, elementCount, arrayTemp ) {

    if ( ( elementCount % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_byGrouptTwo(): `
        + `elementCount ( ${elementCount} ) must be even (i.e. divisible by 2).`
      );

    // Copy the elements to be re-arrange.
    arrayTemp.length = elementCount;
    for ( let i = 0; i < elementCount; ++i ) {
      arrayTemp[ i ] = arrayIn[ indexBegin + i ];
    }

    // Interleave the elements order.
    let elementCountHalf = Math.floor( elementCount / 2 );
    ArrayInterleaver.interleave_asGrouptTwo_from_to( arrayTemp, 0, arrayIn, indexBegin, elementCountHalf );
    return arrayIn;
  }

//!!! ...unfinished... (2022/07/12) interleave_asGrouptTwo_along_last_2nd_axis_from_to()

  /**
   * Rearrange array elements along height by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number} height    The virtual 2D array's height.
   * @param {number} width     The virtual 2D array's width.
   */
  static interleave_asGrouptTwo_alongHeight_from_to( fromArray, toArray, height, width ) {
    let elementCount = height * width;
    if ( elementCount != fromArray.length )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongHeight_from_to(): `
        + `( height * width ) = ( ${height} * ${width} ) = ${elementCount} `
        + ` should be the same as input array length ( ${fromArray.length} ).`
      );

    if ( ( height % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongHeight_from_to(): `
        + `height ( ${height} ) must be even (i.e. divisible by 2).`
      );

    toArray.length = elementCount;
    let elementCountHalf = Math.floor( elementCount / 2 );

    let fromIndex2, toIndex2;
    for ( let y = 0, fromIndex1 = 0, toIndex1 = 0; y < height; y += 2, fromIndex1 += width, toIndex1 = ( toIndex2 + width ) ) {
      fromIndex2 = fromIndex1 + elementCountHalf;
      toIndex2 = toIndex1 + width;
      for ( let i = 0; i < width; ++i ) {
        toArray[ toIndex1 + i ] = fromArray[ fromIndex1 + i ];
        toArray[ toIndex2 + i ] = fromArray[ fromIndex2 + i ];
      }
    }
  }

  /**
   * Rearrange array elements along height by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} arrayIn  The array to be re-arranged. It is an 1D array but represents a 2D array.
   *
   * @param {number} height  The virtual 2D array's height.
   * @param {number} width   The virtual 2D array's width.
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements (from arrayIn) temporarily. Providing this array could reduce memory
   * re-allocation and improve performance.
   *
   * @return {Array}
   *   Retrun the (modified) arrayIn itself.
   */
  static interleave_asGrouptTwo_alongHeight_inPlace( arrayIn, height, width, arrayTemp ) {

    arrayTemp.length = arrayIn.length;
    for ( let i = 0; i < arrayIn.length; ++i ) { // Copy the elements to be re-arrange.
      arrayTemp[ i ] = arrayIn[ i ];
    }

    ArrayInterleaver.interleave_asGrouptTwo_alongHeight_from_to( arrayTemp, arrayIn, height, width );
    return arrayIn;
  }

  /**
   * Rearrange array elements along the last axis by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {Function} pfnInterleaver  The interleaving function (i.e. interleave_asGrouptTwo_from_to or interleave_asGrouptTwo_from_to_undo ).
   * @param {number[]} shape   The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   */
  static interleave_asGrouptTwo_alongLastAxis_from_to_pfnInterleaver( fromArray, toArray, pfnInterleaver, ...shape ) {
    if ( shape.length < 1 ) {
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to_pfnInterleaver(): `
        + `shape.length ( ${shape.length} ) should be at least 1.`
      );
    }

    let elementCount = 1;
    for ( let i = 0; i < shape.length; ++i ) {
      elementCount *= shape[ i ];
    }

    if ( elementCount != fromArray.length ) {
      let shapeString = shape.join( " * " );
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to_pfnInterleaver(): `
        + `shape ( ${shapeString} ) = ${elementCount} `
        + `should be the same as input array length ( ${fromArray.length} ).`
      );
    }

    let lastAxisId = shape.length - 1;
    let lastAxisLength = shape[ lastAxisId ];
    if ( ( lastAxisLength % 2 ) != 0 )
      throw Error( `ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to_pfnInterleaver(): `
        + `shape's last axis length ( ${lastAxisLength} ) must be even (i.e. divisible by 2).`
      );

    toArray.length = elementCount;
    let lastAxisLengthHalf = ( lastAxisLength / 2 );
    for ( let i = 0; i < elementCount; i += lastAxisLength ) {
      pfnInterleaver( fromArray, i, toArray, i, lastAxisLengthHalf );
    }
  }

  /**
   * Rearrange array elements along the last axis by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number[]} shape   The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   */
  static interleave_asGrouptTwo_alongLastAxis_from_to( fromArray, toArray, ...shape ) {
    ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to_pfnInterleaver(
      fromArray, toArray, ArrayInterleaver.interleave_asGrouptTwo_from_to, ...shape );
  }

  /**
   * Rearrange array elements along the last axis by interleaving.
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} arrayIn   The array to be re-arranged. It is an 1D array but represents a N-dimension array.
   * @param {number[]} shape  The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements (from arrayIn) temporarily. Providing this array could reduce memory
   * re-allocation and improve performance.
   *
   * @return {Array}
   *   Retrun the (modified) arrayIn itself.
   */
  static interleave_asGrouptTwo_alongLastAxis_inPlace( arrayIn, arrayTemp, ...shape ) {

    arrayTemp.length = arrayIn.length;
    for ( let i = 0; i < arrayIn.length; ++i ) { // Copy the elements to be re-arrange.
      arrayTemp[ i ] = arrayIn[ i ];
    }

    ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to( arrayTemp, arrayIn, ...shape );
    return arrayIn;
  }

  /**
   * Rearrange array elements along the last axis so that it undo the .interleave_asGrouptTwo_alongLastAxis_from_to().
   *   - Only ( groupCount == 2 ) is supported.
   *
   * @param {Array} fromArray  The source array. It will not be modified. It is the source of copying.
   * @param {Array} toArray    The destination array. It will be modified (i.e. filled data copied from fromArray).
   * @param {number[]} shape   The virtual N-dimension array's shape (e.g. [ width ] or [ height, width ] or [ height, width, depth ].
   */
  static interleave_asGrouptTwo_alongLastAxis_from_to_undo( fromArray, toArray, ...shape ) {
    ArrayInterleaver.interleave_asGrouptTwo_alongLastAxis_from_to_pfnInterleaver(
      fromArray, toArray, ArrayInterleaver.interleave_asGrouptTwo_from_to_undo, ...shape );
  }

}
<|endoftext|>