<commit_before>(function() {

    /**
     * Backbone Forms File editor
     * @author gerardobort <gerardobort@gmail.com>
     * @requires fancy-file
     */
    Backbone.Form.editors.Image = Backbone.Form.editors.Base.extend({

        tagName: 'div',

        defaultValue: '',

        initialize: function(options) {
            var editor = this;

            var options = options || {};

            //Set initial value
            if (options.model) {
                if (!options.key) throw "Missing option: 'key'";

                this.model = options.model;

                // string from model
                this.value = this.model.get(options.key);
            } else if (options.value) {
                // object from list
                this.value = options.value;
            }

            this.$el.removeClass('form-control');
            this.$el.addClass('bbf-image');
            editor.options = options;
            _.extend(editor, _.pick(options, 'key', 'form'));
            var schema = editor.schema = options.schema || {};
            editor.validators = options.validators || schema.validators;

            // taken from: https://developer.mozilla.org/en-US/docs/Web/API/FileReader?redirectlocale=en-US&redirectslug=DOM%2FFileReader#readAsDataURL%28%29
            this.oFReader = new FileReader();
            this.rFilter = /^(?:image\/bmp|image\/cis\-cod|image\/gif|image\/ief|image\/jpeg|image\/jpeg|image\/jpeg|image\/pipeg|image\/png|image\/svg\+xml|image\/tiff|image\/x\-cmu\-raster|image\/x\-cmx|image\/x\-icon|image\/x\-portable\-anymap|image\/x\-portable\-bitmap|image\/x\-portable\-graymap|image\/x\-portable\-pixmap|image\/x\-rgb|image\/x\-xbitmap|image\/x\-xpixmap|image\/x\-xwindowdump)$/i;
        },

        /**
         * Adds the editor to the DOM
         */
        render: function() {
            var editor = this;
            editor.$el.css({ width: 290, display: 'block' });
            editor.$el.toggle(false);
            editor.$el.html(
                '' +
                '' +
                '' +
                '<input name="upload" type="file" data-toggle="fancyfile" />' +
                '<button class="btn btn-danger remove-file"></button>' +
                '

'
            );
            setTimeout(function () { // once appended to the DOM
                editor.$el.toggle(true);
                $('[type="file"]', editor.$el).fancyfile({
                    text  : '',
                    icon  : '',
                    style : 'btn-info',
                    placeholder : 'Select Image…'
                });
                $('.fancy-file', editor.$el).toggle(!editor.value);
                $('.remove-file', editor.$el).toggle(!!editor.value);
                $('.image-preview', editor.$el).toggle(!!editor.value);
            }, 200)
            if (editor.value) {
                if ('object' === typeof editor.value) {
                    $('.img-link', editor.$el).attr('href', '/api/fs.files/' + editor.value._id + '/raw');
                    $('.image-preview', editor.$el).attr('src', '/api/fs.files/' + editor.value._id + '/raw');
                } else {
                    $('.img-link', editor.$el).attr('href', '/api/fs.files/' + editor.value + '/raw');
                    $('.image-preview', editor.$el).attr('src', '/api/fs.files/' + editor.value + '/raw');
                }
            }
            this._delegateEvents();

            return this;
        },

        _delegateEvents: function () {
            var editor = this;

            editor.oFReader.onload = function (oFREvent) {
                $('.image-preview', editor.$el).one('load', function () {
                    $('.image-preview', editor.$el).toggle(true);
                });
                $('.image-preview', editor.$el).attr('src', oFREvent.target.result);
            };

            $('[type="file"]', editor.$el).on('change', function (event) {
                if (this.files.length === 0) { return; }
                var oFile = this.files[0];
                // only in the case of images... TODO
                if (!editor.rFilter.test(oFile.type)) { alert("You must select a valid image file!"); return; }

                editor.oFReader.readAsDataURL(oFile);

                //---
                var xhr = new XMLHttpRequest();
                xhr.open('POST', '/api/fs.files');

                $('.fancy-file', editor.$el).toggle(false);
                xhr.upload.onprogress = function (e) {
                    console.log(e.loaded + ' of ' + e.total);
                    var percent = (e.loaded/(e.total||1))*100 + '%';
                    $('.progress-container .progress-bar', editor.$el)
                        .css('width', percent)
                        .text('Uploading... ' + (percent === '100%' ? '(Waiting for a response)' : percent));
                };

                xhr.onload = function (xhr) {
                    var response = JSON.parse(arguments[0].currentTarget.response);
                    editor.setValue(response.data[0]._id);
                    $('.remove-file', editor.$el).toggle(!!editor.value);
                    $('.fancy-file', editor.$el).toggle(!editor.value);
                    $('.image-preview', editor.$el).toggle(!!editor.value);
                    $('.progress-container', editor.$el).html('');
                    $('.img-link', editor.$el).attr('href', '/api/fs.files/' + editor.value._id + '/raw');
                };

                var form = new FormData();
                var collectionName = $('#collection-form').data('collection-name'); // TODO get this var in a better manner
                form.append(collectionName + '.' + editor.options.key, this.files[0]);
                xhr.send(form);
                $('.progress-container', editor.$el).html(
                    '
' +
                    '

' +
                    '
'
                );
                //---
            });

            $('.remove-file', editor.$el).on('click', function (event) {
                event.preventDefault();
                require(['/third-party/alertify.js/lib/alertify.min.js'], function () {
                    alertify.confirm('Are you sure you want to remove this image?', function (ok) {
                        if (ok) {
                            $.ajax({
                                url: '/api/fs.files/' + editor.value._id,
                                method: 'DELETE'
                            })
                            .success(function () {
                                editor.setValue(null);
                                $('[type="file"]', editor.$el).val(null);
                                $('.image-preview', editor.$el).attr('src', 'about:blank');
                                $('.remove-file', editor.$el).toggle(!!editor.value);
                                $('.fancy-file', editor.$el).toggle(!editor.value);
                                $('.image-preview', editor.$el).toggle(!!editor.value);
                            })
                            .error(function () {
                                alert('An error has occurred.');
                            })
                        }
                    });
                });
            });

        },

        /**
         * Returns the current editor value
         * @return {String}
         */
        getValue: function() {
            // set the file object w/ObjectId
            if (this.value && 'object' === typeof this.value) {
                return this.value._id;
            }
            return this.value;
        },

        /**
         * Sets the value of the form element
         * @param {String}
         */
        setValue: function(value) {
            // set the file object w/ObjectId
            if (value && 'object' === typeof value) {
                value = value._id;
            }
            this.value = value;
        },

        focus: function () {
            $('[type="file"]', this.$el).get(0).focus();
        },

        blur: function () {
            $('[type="file"]', this.$el).get(0).blur();
        }

    });

}());
<commit_msg>image editor - now using s3 for previews<commit_after>(function() {

    /**
     * Backbone Forms File editor
     * @author gerardobort <gerardobort@gmail.com>
     * @requires fancy-file
     */
    Backbone.Form.editors.Image = Backbone.Form.editors.Base.extend({

        tagName: 'div',

        defaultValue: '',

        initialize: function(options) {
            var editor = this;

            var options = options || {};

            //Set initial value
            if (options.model) {
                if (!options.key) throw "Missing option: 'key'";

                this.model = options.model;

                // string from model
                this.value = this.model.get(options.key);
            } else if (options.value) {
                // object from list
                this.value = options.value;
            }

            this.$el.removeClass('form-control');
            this.$el.addClass('bbf-image');
            editor.options = options;
            _.extend(editor, _.pick(options, 'key', 'form'));
            var schema = editor.schema = options.schema || {};
            editor.validators = options.validators || schema.validators;

            // taken from: https://developer.mozilla.org/en-US/docs/Web/API/FileReader?redirectlocale=en-US&redirectslug=DOM%2FFileReader#readAsDataURL%28%29
            this.oFReader = new FileReader();
            this.rFilter = /^(?:image\/bmp|image\/cis\-cod|image\/gif|image\/ief|image\/jpeg|image\/jpeg|image\/jpeg|image\/pipeg|image\/png|image\/svg\+xml|image\/tiff|image\/x\-cmu\-raster|image\/x\-cmx|image\/x\-icon|image\/x\-portable\-anymap|image\/x\-portable\-bitmap|image\/x\-portable\-graymap|image\/x\-portable\-pixmap|image\/x\-rgb|image\/x\-xbitmap|image\/x\-xpixmap|image\/x\-xwindowdump)$/i;
        },

        /**
         * Adds the editor to the DOM
         */
        render: function() {
            var editor = this;
            editor.$el.css({ width: 290, display: 'block' });
            editor.$el.toggle(false);
            editor.$el.html(
                '' +
                '' +
                '' +
                '<input name="upload" type="file" data-toggle="fancyfile" />' +
                '<button class="btn btn-danger remove-file"></button>' +
                '

'
            );
            setTimeout(function () { // once appended to the DOM
                editor.$el.toggle(true);
                $('[type="file"]', editor.$el).fancyfile({
                    text  : '',
                    icon  : '',
                    style : 'btn-info',
                    placeholder : 'Select Image…'
                });
                $('.fancy-file', editor.$el).toggle(!editor.value);
                $('.remove-file', editor.$el).toggle(!!editor.value);
                $('.image-preview', editor.$el).toggle(!!editor.value);
            }, 200)
            if (editor.value) {
                if ('object' === typeof editor.value) {
                    $('.img-link', editor.$el).attr('href', '/api/fs.files/' + editor.value._id + '/raw');
                    if (editor.value.metadata && editor.value.metadata.s3_url) {
                        $('.image-preview', editor.$el).attr('src', editor.value.metadata.s3_url);
                    } else {
                        $('.image-preview', editor.$el).attr('src', '/api/fs.files/' + editor.value._id + '/raw');
                    }
                } else {
                    $('.img-link', editor.$el).attr('href', '/api/fs.files/' + editor.value + '/raw');
                    $('.image-preview', editor.$el).attr('src', '/api/fs.files/' + editor.value + '/raw');
                }
            }
            this._delegateEvents();

            return this;
        },

        _delegateEvents: function () {
            var editor = this;

            editor.oFReader.onload = function (oFREvent) {
                $('.image-preview', editor.$el).one('load', function () {
                    $('.image-preview', editor.$el).toggle(true);
                });
                $('.image-preview', editor.$el).attr('src', oFREvent.target.result);
            };

            $('[type="file"]', editor.$el).on('change', function (event) {
                if (this.files.length === 0) { return; }
                var oFile = this.files[0];
                // only in the case of images... TODO
                if (!editor.rFilter.test(oFile.type)) { alert("You must select a valid image file!"); return; }

                editor.oFReader.readAsDataURL(oFile);

                //---
                var xhr = new XMLHttpRequest();
                xhr.open('POST', '/api/fs.files');

                $('.fancy-file', editor.$el).toggle(false);
                xhr.upload.onprogress = function (e) {
                    console.log(e.loaded + ' of ' + e.total);
                    var percent = (e.loaded/(e.total||1))*100 + '%';
                    $('.progress-container .progress-bar', editor.$el)
                        .css('width', percent)
                        .text('Uploading... ' + (percent === '100%' ? '(Waiting for a response)' : percent));
                };

                xhr.onload = function (xhr) {
                    var response = JSON.parse(arguments[0].currentTarget.response);
                    editor.setValue(response.data[0]._id);
                    $('.remove-file', editor.$el).toggle(!!editor.value);
                    $('.fancy-file', editor.$el).toggle(!editor.value);
                    $('.image-preview', editor.$el).toggle(!!editor.value);
                    $('.progress-container', editor.$el).html('');
                    $('.img-link', editor.$el).attr('href', '/api/fs.files/' + editor.value._id + '/raw');
                };

                var form = new FormData();
                var collectionName = $('#collection-form').data('collection-name'); // TODO get this var in a better manner
                form.append(collectionName + '.' + editor.options.key, this.files[0]);
                xhr.send(form);
                $('.progress-container', editor.$el).html(
                    '
' +
                    '

' +
                    '
'
                );
                //---
            });

            $('.remove-file', editor.$el).on('click', function (event) {
                event.preventDefault();
                require(['/third-party/alertify.js/lib/alertify.min.js'], function () {
                    alertify.confirm('Are you sure you want to remove this image?', function (ok) {
                        if (ok) {
                            $.ajax({
                                url: '/api/fs.files/' + editor.value._id,
                                method: 'DELETE'
                            })
                            .success(function () {
                                editor.setValue(null);
                                $('[type="file"]', editor.$el).val(null);
                                $('.image-preview', editor.$el).attr('src', 'about:blank');
                                $('.remove-file', editor.$el).toggle(!!editor.value);
                                $('.fancy-file', editor.$el).toggle(!editor.value);
                                $('.image-preview', editor.$el).toggle(!!editor.value);
                            })
                            .error(function () {
                                alert('An error has occurred.');
                            })
                        }
                    });
                });
            });

        },

        /**
         * Returns the current editor value
         * @return {String}
         */
        getValue: function() {
            // set the file object w/ObjectId
            if (this.value && 'object' === typeof this.value) {
                return this.value._id;
            }
            return this.value;
        },

        /**
         * Sets the value of the form element
         * @param {String}
         */
        setValue: function(value) {
            // set the file object w/ObjectId
            if (value && 'object' === typeof value) {
                value = value._id;
            }
            this.value = value;
        },

        focus: function () {
            $('[type="file"]', this.$el).get(0).focus();
        },

        blur: function () {
            $('[type="file"]', this.$el).get(0).blur();
        }

    });

}());
<|endoftext|>


<commit_before>/// <reference path="http://code.jquery.com/jquery-1.4.1-vsdoc.js" />
/*
* Print Element Plugin 1.2
*
* Copyright (c) 2010 Erik Zaadi
*
* Inspired by PrintArea (http://plugins.jquery.com/project/PrintArea) and
* http://stackoverflow.com/questions/472951/how-do-i-print-an-iframe-from-javascript-in-safari-chrome
*
* Home Page : http://projects.erikzaadi/jQueryPlugins/jQuery.printElement
* Issues (bug reporting) : http://github.com/erikzaadi/jQueryPlugins/issues/labels/printElement
* jQuery plugin page : http://plugins.jquery.com/project/printElement
*
* Thanks to David B (http://github.com/ungenio) and icgJohn (http://www.blogger.com/profile/11881116857076484100)
* For their great contributions!
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Note, Iframe Printing is not supported in Opera and Chrome 3.0, a popup window will be shown instead
*/
; (function (window, undefined) {
    var document = window["document"];
    var $ = window["jQuery"];
    $.fn["printElement"] = function (options) {
        var mainOptions = $.extend({}, $.fn["printElement"]["defaults"], options);
        //iframe mode is not supported for opera and chrome 3.0 (it prints the entire page).
        //http://www.google.com/support/forum/p/Webmasters/thread?tid=2cb0f08dce8821c3&hl=en
        if (mainOptions["printMode"] == 'iframe') {
            if ($.browser.opera || (/chrome/.test(navigator.userAgent.toLowerCase())))
                mainOptions["printMode"] = 'popup';
        }
        //Remove previously printed iframe if exists
        $("[id^='printElement_']").remove();

        return this.each(function () {
            //Support Metadata Plug-in if available
            var opts = $.meta ? $.extend({}, mainOptions, $(this).data()) : mainOptions;
            _printElement($(this), opts);
        });
    };
    $.fn["printElement"]["defaults"] = {
        "printMode": 'iframe', //Usage : iframe / popup
        "pageTitle": '', //Print Page Title
        "overrideElementCSS": null,
        /* Can be one of the following 3 options:
* 1 : boolean (pass true for stripping all css linked)
* 2 : array of $.fn.printElement.cssElement (s)
* 3 : array of strings with paths to alternate css files (optimized for print)
*/
        "printBodyOptions": {
            "styleToAdd": 'padding:10px;margin:10px;', //style attributes to add to the body of print document
            "classNameToAdd": '' //css class to add to the body of print document
        },
        "leaveOpen": false, // in case of popup, leave the print page open or not
        "iframeElementOptions": {
            "styleToAdd": 'border:none;position:absolute;width:0px;height:0px;bottom:0px;left:0px;', //style attributes to add to the iframe element
            "classNameToAdd": '' //css class to add to the iframe element
        }
    };
    $.fn["printElement"]["cssElement"] = {
        "href": '',
        "media": ''
    };
    function _printElement(element, opts) {
        //Create markup to be printed
        var html = _getMarkup(element, opts);

        var popupOrIframe = null;
        var documentToWriteTo = null;
        if (opts["printMode"].toLowerCase() == 'popup') {
            popupOrIframe = window.open('about:blank', 'printElementWindow', 'width=650,height=440,scrollbars=yes');
            documentToWriteTo = popupOrIframe.document;
        }
        else {
            //The random ID is to overcome a safari bug http://www.cjboco.com.sharedcopy.com/post.cfm/442dc92cd1c0ca10a5c35210b8166882.html
            var printElementID = "printElement_" + (Math.round(Math.random() * 99999)).toString();
            //Native creation of the element is faster..
            var iframe = document.createElement('IFRAME');
            $(iframe).attr({
                style: opts["iframeElementOptions"]["styleToAdd"],
                id: printElementID,
                className: opts["iframeElementOptions"]["classNameToAdd"],
                frameBorder: 0,
                scrolling: 'no',
                src: 'about:blank'
            });
            document.body.appendChild(iframe);
            documentToWriteTo = (iframe.contentWindow || iframe.contentDocument);
            if (documentToWriteTo.document)
                documentToWriteTo = documentToWriteTo.document;
            iframe = document.frames ? document.frames[printElementID] : document.getElementById(printElementID);
            popupOrIframe = iframe.contentWindow || iframe;
        }
        focus();
        documentToWriteTo.open();
        documentToWriteTo.write(html);
        documentToWriteTo.close();
        _callPrint(popupOrIframe);
    };

    function _callPrint(element) {
        if (element && element["printPage"])
            element["printPage"]();
        else
            setTimeout(function () {
                _callPrint(element);
            }, 50);
    }

    function _getElementHTMLIncludingFormElements(element) {
        var $element = $(element);
        //Radiobuttons and checkboxes
        /*$(":checked", $element).each(function () {
            this.setAttribute('checked', 'checked');
        });
        //simple text inputs
        $("input[type='text']", $element).each(function () {
            this.setAttribute('value', $(this).val());
        });
        $("select", $element).each(function () {
            var $select = $(this);
            $("option", $select).each(function () {
                if ($select.val() == $(this).val())
                    this.setAttribute('selected', 'selected');
            });
        });
        $("textarea", $element).each(function () {
            //Thanks http://blog.ekini.net/2009/02/24/jquery-getting-the-latest-textvalue-inside-a-textarea/
            var value = $(this).attr('value');
            //fix for issue 7 (http://plugins.jquery.com/node/13503 and http://github.com/erikzaadi/jQueryPlugins/issues#issue/7)
            if ($.browser.mozilla && this.firstChild)
                this.firstChild.textContent = value;
            else
                this.innerHTML = value;
        });*/
        //http://dbj.org/dbj/?p=91
        var elementHtml = $('

').append($element.clone()).html();
        return elementHtml;
    }

    function _getBaseHref() {
        var port = (window.location.port) ? ':' + window.location.port : '';
        return window.location.protocol + '//' + window.location.hostname + port + window.location.pathname;
    }

    function _getMarkup(element, opts) {
        var $element = $(element);
        var elementHtml = _getElementHTMLIncludingFormElements(element);

        var html = new Array();
        html.push('<title>' + opts["pageTitle"] + '</title>');
        if (opts["overrideElementCSS"]) {
            if (opts["overrideElementCSS"].length > 0) {
                for (var x = 0; x < opts["overrideElementCSS"].length; x++) {
                    var current = opts["overrideElementCSS"][x];
                    if (typeof (current) == 'string')
                        html.push('<link type="text/css" rel="stylesheet" href="' + current + '" >');
                    else
                        html.push('<link type="text/css" rel="stylesheet" href="' + current["href"] + '" media="' + current["media"] + '" >');
                }
            }
        }
        else {
            $("link", document).filter(function () {
                return $(this).attr("rel").toLowerCase() == "stylesheet";
            }).each(function () {
                html.push('<link type="text/css" rel="stylesheet" href="' + $(this).attr("href") + '" media="' + $(this).attr('media') + '" >');
            });
        }
        //Ensure that relative links work
        html.push('<base href="' + _getBaseHref() + '" />');
        html.push('');
        html.push('
' + elementHtml + '
');
        html.push('');
        html.push('');

        return html.join('');
    };
})(window);<commit_msg>bugfix in printElement<commit_after>/// <reference path="http://code.jquery.com/jquery-1.4.1-vsdoc.js" />
/*
* Print Element Plugin 1.2
*
* Copyright (c) 2010 Erik Zaadi
*
* Inspired by PrintArea (http://plugins.jquery.com/project/PrintArea) and
* http://stackoverflow.com/questions/472951/how-do-i-print-an-iframe-from-javascript-in-safari-chrome
*
* Home Page : http://projects.erikzaadi/jQueryPlugins/jQuery.printElement
* Issues (bug reporting) : http://github.com/erikzaadi/jQueryPlugins/issues/labels/printElement
* jQuery plugin page : http://plugins.jquery.com/project/printElement
*
* Thanks to David B (http://github.com/ungenio) and icgJohn (http://www.blogger.com/profile/11881116857076484100)
* For their great contributions!
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Note, Iframe Printing is not supported in Opera and Chrome 3.0, a popup window will be shown instead
*/
; (function (window, undefined) {
    var document = window["document"];
    var $ = window["jQuery"];
    $.fn["printElement"] = function (options) {
        var mainOptions = $.extend({}, $.fn["printElement"]["defaults"], options);
        //iframe mode is not supported for opera and chrome 3.0 (it prints the entire page).
        //http://www.google.com/support/forum/p/Webmasters/thread?tid=2cb0f08dce8821c3&hl=en
        if (mainOptions["printMode"] == 'iframe') {
            if ($.browser.opera || (/chrome/.test(navigator.userAgent.toLowerCase())))
                mainOptions["printMode"] = 'popup';
        }
        //Remove previously printed iframe if exists
        $("[id^='printElement_']").remove();

        return this.each(function () {
            //Support Metadata Plug-in if available
            var opts = $.meta ? $.extend({}, mainOptions, $(this).data()) : mainOptions;
            _printElement($(this), opts);
        });
    };
    $.fn["printElement"]["defaults"] = {
        "printMode": 'iframe', //Usage : iframe / popup
        "pageTitle": '', //Print Page Title
        "overrideElementCSS": null,
        /* Can be one of the following 3 options:
* 1 : boolean (pass true for stripping all css linked)
* 2 : array of $.fn.printElement.cssElement (s)
* 3 : array of strings with paths to alternate css files (optimized for print)
*/
        "printBodyOptions": {
            "styleToAdd": 'padding:10px;margin:10px;', //style attributes to add to the body of print document
            "classNameToAdd": '' //css class to add to the body of print document
        },
        "leaveOpen": false, // in case of popup, leave the print page open or not
        "iframeElementOptions": {
            "styleToAdd": 'border:none;position:absolute;width:0px;height:0px;bottom:0px;left:0px;', //style attributes to add to the iframe element
            "classNameToAdd": '' //css class to add to the iframe element
        }
    };
    $.fn["printElement"]["cssElement"] = {
        "href": '',
        "media": ''
    };
    function _printElement(element, opts) {
        //Create markup to be printed
        var html = _getMarkup(element, opts);

        var popupOrIframe = null;
        var documentToWriteTo = null;
        if (opts["printMode"].toLowerCase() == 'popup') {
            popupOrIframe = window.open('about:blank', 'printElementWindow', 'width=650,height=440,scrollbars=yes');
            documentToWriteTo = popupOrIframe.document;
        }
        else {
            //The random ID is to overcome a safari bug http://www.cjboco.com.sharedcopy.com/post.cfm/442dc92cd1c0ca10a5c35210b8166882.html
            var printElementID = "printElement_" + (Math.round(Math.random() * 99999)).toString();
            //Native creation of the element is faster..
            var iframe = document.createElement('IFRAME');
            $(iframe).attr({
                style: opts["iframeElementOptions"]["styleToAdd"],
                id: printElementID,
                className: opts["iframeElementOptions"]["classNameToAdd"],
                frameBorder: 0,
                scrolling: 'no',
                src: 'about:blank'
            });
            document.body.appendChild(iframe);
            documentToWriteTo = (iframe.contentWindow || iframe.contentDocument);
            if (documentToWriteTo.document)
                documentToWriteTo = documentToWriteTo.document;
            iframe = document.frames ? document.frames[printElementID] : document.getElementById(printElementID);
            popupOrIframe = iframe.contentWindow || iframe;
        }
        focus();
        documentToWriteTo.open();
        documentToWriteTo.write(html);
        documentToWriteTo.close();
        _callPrint(popupOrIframe);
    };

    function _callPrint(element) {
        if (element && element["printPage"])
            element["printPage"]();
        else
            setTimeout(function () {
                _callPrint(element);
            }, 50);
    }

    function _getElementHTMLIncludingFormElements(element) {
        var $element = $(element);
        //Radiobuttons and checkboxes
        /*$(":checked", $element).each(function () {
            this.setAttribute('checked', 'checked');
        });
        //simple text inputs
        $("input[type='text']", $element).each(function () {
            this.setAttribute('value', $(this).val());
        });
        $("select", $element).each(function () {
            var $select = $(this);
            $("option", $select).each(function () {
                if ($select.val() == $(this).val())
                    this.setAttribute('selected', 'selected');
            });
        });
        $("textarea", $element).each(function () {
            //Thanks http://blog.ekini.net/2009/02/24/jquery-getting-the-latest-textvalue-inside-a-textarea/
            var value = $(this).attr('value');
            //fix for issue 7 (http://plugins.jquery.com/node/13503 and http://github.com/erikzaadi/jQueryPlugins/issues#issue/7)
            if ($.browser.mozilla && this.firstChild)
                this.firstChild.textContent = value;
            else
                this.innerHTML = value;
        });*/
        //http://dbj.org/dbj/?p=91
        var elementHtml = $('

').append($element.clone()).html();
        return elementHtml;
    }

    function _getBaseHref() {
        var port = (window.location.port) ? ':' + window.location.port : '';
        return window.location.protocol + '//' + window.location.hostname + port + window.location.pathname;
    }

    function _getMarkup(element, opts) {
        var $element = $(element);
        var elementHtml = _getElementHTMLIncludingFormElements(element);

        var html = new Array();
        html.push('<title>' + opts["pageTitle"] + '</title>');
        // if (opts["overrideElementCSS"]) {
        //     if (opts["overrideElementCSS"].length > 0) {
        //         for (var x = 0; x < opts["overrideElementCSS"].length; x++) {
        //             var current = opts["overrideElementCSS"][x];
        //             if (typeof (current) == 'string')
        //                 html.push('<link type="text/css" rel="stylesheet" href="' + current + '" >');
        //             else
        //                 html.push('<link type="text/css" rel="stylesheet" href="' + current["href"] + '" media="' + current["media"] + '" >');
        //         }
        //     }
        // }
        // else {
        //     $("link", document).filter(function () {
        //         return $(this).attr("rel").toLowerCase() == "stylesheet";
        //     }).each(function () {
        //         html.push('<link type="text/css" rel="stylesheet" href="' + $(this).attr("href") + '" media="' + $(this).attr('media') + '" >');
        //     });
        // }
        //Ensure that relative links work
        html.push('<base href="' + _getBaseHref() + '" />');
        html.push('');
        html.push('
' + elementHtml + '
');
        html.push('');
        html.push('');

        return html.join('');
    };
})(window);<|endoftext|>


<commit_before>var DeveloperPlayground = DeveloperPlayground || {};

DeveloperPlayground.StartQueryBuilderView = Backbone.View.extend({
    el: "#developer-playground",

    template: _.template($('#playground-template').html()),

    events: {
        'change .form-control#entity': 'changeParam',
        'change .form-control#parameter': 'changeName',
        'click .btn' : 'submit'
    },

    initialize: function () {
        this.operators = new DeveloperPlayground.Operators();
        this.stops = new DeveloperPlayground.Stops();
        this.render();
    },

    render: function() {
        this.$el.html(this.template());
        $(".form-control#operator-name").hide();
        return this;
    },

    changeParam: function() {
        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var selectValues = {
            "stops": {
                "bbox": "",
                "hello": "",
            },
            "operators": {
                "hello": "",
                "name": "",
            }
        };

        if($entitySelect.val() != "operators") {
            $(".form-control#operator-name").hide();
        }

        $parameterSelect.empty().append(function() {
            var output = '';
            $.each(selectValues[$entitySelect.val()], function(key, value) {
                output += '<option>' + key + '</option>';
            });
            return output;
        });
    },

    changeName: function() {
        var $parameterSelect = $('select.form-control#parameter');
        var $nameSelect = $('select.form-control#operator-name');
        var selectName = {
            "name": {
                "BART": "",
                "AC Transit": "",
                "Muni": "",
            }
        };

        if($parameterSelect.val() == "name") {
            $(".form-control#operator-name").show();
        } else {
            $(".form-control#operator-name").hide();
        }

        $nameSelect.empty().append(function() {
            var output = '';
            $.each(selectName[$parameterSelect.val()], function(key, value) {
                output += '<option>' + key + '</option>';
            });
            return output;
        });
    },

    submit: function() {
        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var $nameSelect = $('select.form-control#operator-name');
        var collection;

        if($parameterSelect.val() == "bbox") {
            this.mapview = new DeveloperPlayground.MapView({collection: collection});
            this.mapview.render();
            this.stops.setQueryParameters({
                    url: 'http://localhost:4567/api/v1/'+$entitySelect.val()+'.json?bbox=-122.39893913269043,37.76651662158726,-122.38070011138915,37.77178331201861'
                });
            collection = this.stops;
            console.log("bounds", this.mapview.bounds);
        } else if ($parameterSelect.val() == "name") {
            if ($entitySelect.val() == 'operators') {
                this.operators.setQueryParameters({
                    identifier: $nameSelect.val()
                });
                collection = this.operators;
            }
        } else if($parameterSelect.val() == "hello") {
            alert("please select a different parameter");
        } else {
            alert("please select a parameter");
        }

        collection.fetch();

        if (!this.mapview){
            this.mapview = new DeveloperPlayground.MapView({collection: collection});
            this.mapview.render();

        } else {
            // this.mapview.removeFeatureGroup();
            // this.mapview.layergroup.clearLayers();
            this.mapview.featuregroup.clearLayers();
            this.mapview.initialize({collection: collection});

        }

        if ('undefined' !== typeof this.tableview) this.tableview.close();

        this.tableview = new DeveloperPlayground.TableView({collection: collection});
        this.headerView = new DeveloperPlayground.HeaderView({collection: collection});

    }

});<commit_msg>grabbing and printing to console the map bounds<commit_after>var DeveloperPlayground = DeveloperPlayground || {};

DeveloperPlayground.StartQueryBuilderView = Backbone.View.extend({
    el: "#developer-playground",

    template: _.template($('#playground-template').html()),

    events: {
        'change .form-control#entity': 'changeParam',
        'change .form-control#parameter': 'changeName',
        'click .btn' : 'submit'
    },

    initialize: function () {
        this.operators = new DeveloperPlayground.Operators();
        this.stops = new DeveloperPlayground.Stops();
        this.render();
    },

    render: function() {
        this.$el.html(this.template());
        $(".form-control#operator-name").hide();
        return this;
    },

    changeParam: function() {
        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var selectValues = {
            "stops": {
                "bbox": "",
                "hello": "",
            },
            "operators": {
                "hello": "",
                "name": "",
            }
        };

        if($entitySelect.val() != "operators") {
            $(".form-control#operator-name").hide();
        }

        $parameterSelect.empty().append(function() {
            var output = '';
            $.each(selectValues[$entitySelect.val()], function(key, value) {
                output += '<option>' + key + '</option>';
            });
            return output;
        });
    },

    changeName: function() {
        var $parameterSelect = $('select.form-control#parameter');
        var $nameSelect = $('select.form-control#operator-name');
        var selectName = {
            "name": {
                "BART": "",
                "AC Transit": "",
                "Muni": "",
            }
        };

        if($parameterSelect.val() == "name") {
            $(".form-control#operator-name").show();
        } else {
            $(".form-control#operator-name").hide();
        }

        $nameSelect.empty().append(function() {
            var output = '';
            $.each(selectName[$parameterSelect.val()], function(key, value) {
                output += '<option>' + key + '</option>';
            });
            return output;
        });
    },

    submit: function() {
        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var $nameSelect = $('select.form-control#operator-name');
        var collection;

        if($parameterSelect.val() == "bbox") {
            // this.mapview = new DeveloperPlayground.MapView({collection: collection});
            // this.mapview.render();
            this.stops.setQueryParameters({
                    url: 'http://localhost:4567/api/v1/'+$entitySelect.val()+'.json?bbox=-122.39893913269043,37.76651662158726,-122.38070011138915,37.77178331201861'
                });
            collection = this.stops;
        } else if ($parameterSelect.val() == "name") {
            if ($entitySelect.val() == 'operators') {
                this.operators.setQueryParameters({
                    identifier: $nameSelect.val()
                });
                collection = this.operators;
            }
        } else if($parameterSelect.val() == "hello") {
            alert("please select a different parameter");
        } else {
            alert("please select a parameter");
        }

        collection.fetch();

        if (!this.mapview){
            this.mapview = new DeveloperPlayground.MapView({collection: collection});
            this.mapview.render();
            console.log("bounds", this.mapview.bounds);

        } else {
            // this.mapview.removeFeatureGroup();
            // this.mapview.layergroup.clearLayers();
            this.mapview.featuregroup.clearLayers();
            this.mapview.initialize({collection: collection});

        }

        if ('undefined' !== typeof this.tableview) this.tableview.close();

        this.tableview = new DeveloperPlayground.TableView({collection: collection});
        this.headerView = new DeveloperPlayground.HeaderView({collection: collection});

    }

});<|endoftext|>


<commit_before>let map = L.map('map');

let custom_attribution = `${document.title} (<a href="https://github.com/frafra/is-osm-uptodate">source code</a>)`;
let OpenStreetMapLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: `${custom_attribution} | © <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>`,
  maxZoom: 19,
  minZoom: 1
});

OpenStreetMapLayer.addTo(map);
map.setView([45.46423, 9.19073], 18); // Duomo di Milano

L.easyButton('fa-refresh', (btn, map) => {
    getNodes();
}).addTo(map);

let info = L.control();
info.onAdd = map => {
  this.div = L.DomUtil.create('div', 'info');
  return this.div;
};
info.update = message => {
  this.div.innerHTML = message;
  this.div.innerHTML += `

      Old

      New

`;
};
info.addTo(map);

let nodes = L.geoJSON();

function openOldestMarker() {
  map.panTo(window.oldestMarker.getLatLng());
  window.oldestMarker.openPopup();
}

function getNodes() {
  info.update('Loading nodes...');
  let bounds = map.getBounds();
  let west = bounds.getWest();
  let east = bounds.getEast();
  let south = bounds.getSouth();
  let north = bounds.getNorth();
  let url = `/api/getNodes?minx=${west}&maxx=${east}&miny=${south}&maxy=${north}`;
  fetch(url).then(response => {
    return response.json();
  }).then(results => {
    nodes.remove();
    let oldest = new Date();
    let oldestNode;
    for (let index in results.features) {
      let feature = results.features[index];
      let date = new Date(feature.properties.timestamp);
      if (date < oldest) {
        oldest = date;
        oldestNode = feature;
      }
    }
    let timestamp = oldest.toISOString().slice(0, 10);
    info.update(`Oldest node:
      #${oldestNode.properties.node_id}
      (${timestamp})`);
    let range = (new Date()).getTime()-oldest.getTime();
    nodes = L.geoJSON(results, {
        pointToLayer: (feature, latlng) => {
            let attributes_list = `<ul>`;
            for (let key in feature.properties.attributes) {
              let value = feature.properties.attributes[key];
              attributes_list += `
${key}:
               ${value}
`;
            }
            attributes_list += `</ul>`;
            let popup = `

Node #${feature.properties.node_id}

              Last edit: ${feature.properties.timestamp}

              User:
                ${feature.properties.user}
                (${feature.properties.uid})

              Version: ${feature.properties.version}

              Attributes:
                ${attributes_list}

                Edit  |
                History |
                Details

            `
            let now = new Date(feature.properties.timestamp);
            let seconds = now.getTime()-oldest.getTime();
            let computed = 240*seconds/range;
            let marker = L.circleMarker(latlng, {
              radius: 5,
              fillColor: `hsl(${computed}, 100%, 50%)`,
              color: "#555",
              weight: 1,
              opacity: 1,
              fillOpacity: 1
            });
            marker.bindPopup(popup);
            if (feature.properties.node_id == oldestNode.properties.node_id) {
              window.oldestMarker = marker;
            }
            return marker;
        }
    });
    nodes.addTo(map);
  });
}

getNodes();
<commit_msg>Hide the color bar when loading<commit_after>let map = L.map('map');

let custom_attribution = `${document.title} (<a href="https://github.com/frafra/is-osm-uptodate">source code</a>)`;
let OpenStreetMapLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: `${custom_attribution} | © <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>`,
  maxZoom: 19,
  minZoom: 1
});

OpenStreetMapLayer.addTo(map);
map.setView([45.46423, 9.19073], 18); // Duomo di Milano

L.easyButton('fa-refresh', (btn, map) => {
    getNodes();
}).addTo(map);

let info = L.control();
info.onAdd = map => {
  this.div = L.DomUtil.create('div', 'info');
  return this.div;
};
info.update = message => {
  this.div.innerHTML = message;
};
info.addTo(map);

let nodes = L.geoJSON();

function openOldestMarker() {
  map.panTo(window.oldestMarker.getLatLng());
  window.oldestMarker.openPopup();
}

function getNodes() {
  info.update('Loading nodes...');
  let bounds = map.getBounds();
  let west = bounds.getWest();
  let east = bounds.getEast();
  let south = bounds.getSouth();
  let north = bounds.getNorth();
  let url = `/api/getNodes?minx=${west}&maxx=${east}&miny=${south}&maxy=${north}`;
  fetch(url).then(response => {
    return response.json();
  }).then(results => {
    nodes.remove();
    let oldest = new Date();
    let oldestNode;
    for (let index in results.features) {
      let feature = results.features[index];
      let date = new Date(feature.properties.timestamp);
      if (date < oldest) {
        oldest = date;
        oldestNode = feature;
      }
    }
    let timestamp = oldest.toISOString().slice(0, 10);
    info.update(`
      Oldest node:
      #${oldestNode.properties.node_id}
      (${timestamp})

        Old

        New

    `);
    let range = (new Date()).getTime()-oldest.getTime();
    nodes = L.geoJSON(results, {
        pointToLayer: (feature, latlng) => {
            let attributes_list = `<ul>`;
            for (let key in feature.properties.attributes) {
              let value = feature.properties.attributes[key];
              attributes_list += `
${key}:
               ${value}
`;
            }
            attributes_list += `</ul>`;
            let popup = `

Node #${feature.properties.node_id}

              Last edit: ${feature.properties.timestamp}

              User:
                ${feature.properties.user}
                (${feature.properties.uid})

              Version: ${feature.properties.version}

              Attributes:
                ${attributes_list}

                Edit  |
                History |
                Details

            `
            let now = new Date(feature.properties.timestamp);
            let seconds = now.getTime()-oldest.getTime();
            let computed = 240*seconds/range;
            let marker = L.circleMarker(latlng, {
              radius: 5,
              fillColor: `hsl(${computed}, 100%, 50%)`,
              color: "#555",
              weight: 1,
              opacity: 1,
              fillOpacity: 1
            });
            marker.bindPopup(popup);
            if (feature.properties.node_id == oldestNode.properties.node_id) {
              window.oldestMarker = marker;
            }
            return marker;
        }
    });
    nodes.addTo(map);
  });
}

getNodes();
<|endoftext|>


<commit_before>/**
 * Blueprint API Configuration
 * (sails.config.blueprints)
 *
 * These settings are for the global configuration of blueprint routes and
 * request options (which impact the behavior of blueprint actions).
 *
 * You may also override any of these settings on a per-controller basis
 * by defining a '_config' key in your controller definition, and assigning it
 * a configuration object with overrides for the settings in this file.
 * A lot of the configuration options below affect so-called "CRUD methods",
 * or your controllers' `find`, `create`, `update`, and `destroy` actions.
 *
 * It's important to realize that, even if you haven't defined these yourself, as long as
 * a model exists with the same name as the controller, Sails will respond with built-in CRUD
 * logic in the form of a JSON API, including support for sort, pagination, and filtering.
 *
 * For more information on the blueprint API, check out:
 * http://sailsjs.org/#!/documentation/reference/blueprint-api
 *
 * For more information on the settings in this file, see:
 * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.blueprints.html
 *
 */

module.exports.blueprints = {

  /***************************************************************************
  *                                                                          *
  * Action routes speed up the backend development workflow by               *
  * eliminating the need to manually bind routes. When enabled, GET, POST,   *
  * PUT, and DELETE routes will be generated for every one of a controller's *
  * actions.                                                                 *
  *                                                                          *
  * If an `index` action exists, additional naked routes will be created for *
  * it. Finally, all `actions` blueprints support an optional path           *
  * parameter, `id`, for convenience.                                        *
  *                                                                          *
  * `actions` are enabled by default, and can be OK for production--         *
  * however, if you'd like to continue to use controller/action autorouting  *
  * in a production deployment, you must take great care not to              *
  * inadvertently expose unsafe/unintentional controller logic to GET        *
  * requests.                                                                *
  *                                                                          *
  ***************************************************************************/

  // actions: true,

  /***************************************************************************
  *                                                                          *
  * RESTful routes (`sails.config.blueprints.rest`)                          *
  *                                                                          *
  * REST blueprints are the automatically generated routes Sails uses to     *
  * expose a conventional REST API on top of a controller's `find`,          *
  * `create`, `update`, and `destroy` actions.                               *
  *                                                                          *
  * For example, a BoatController with `rest` enabled generates the          *
  * following routes:                                                        *
  * :::::::::::::::::::::::::::::::::::::::::::::::::::::::                  *
  *  GET /boat -> BoatController.find                                        *
  *  GET /boat/:id -> BoatController.findOne                                 *
  *  POST /boat -> BoatController.create                                     *
  *  PUT /boat/:id -> BoatController.update                                  *
  *  DELETE /boat/:id -> BoatController.destroy                              *
  *                                                                          *
  * `rest` blueprint routes are enabled by default, and are suitable for use *
  * in a production scenario, as long you take standard security precautions *
  * (combine w/ policies, etc.)                                              *
  *                                                                          *
  ***************************************************************************/

  // rest: true,

  /***************************************************************************
  *                                                                          *
  * Shortcut routes are simple helpers to provide access to a                *
  * controller's CRUD methods from your browser's URL bar. When enabled,     *
  * GET, POST, PUT, and DELETE routes will be generated for the              *
  * controller's`find`, `create`, `update`, and `destroy` actions.           *
  *                                                                          *
  * `shortcuts` are enabled by default, but should be disabled in            *
  * production.                                                              *
  *                                                                          *
  ***************************************************************************/

  // shortcuts: true,

  /***************************************************************************
  *                                                                          *
  * An optional mount path for all blueprint routes on a controller,         *
  * including `rest`, `actions`, and `shortcuts`. This allows you to take    *
  * advantage of blueprint routing, even if you need to namespace your API   *
  * methods.                                                                 *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  ***************************************************************************/

  // prefix: '',

  /***************************************************************************
   *                                                                          *
   * An optional mount path for all REST blueprint routes on a controller.    *
   * And it do not include `actions` and `shortcuts` routes.                  *
   * This allows you to take advantage of REST blueprint routing,             *
   * even if you need to namespace your RESTful API methods                   *
   *                                                                          *
   ***************************************************************************/

  // restPrefix: '',

  /***************************************************************************
  *                                                                          *
  * Whether to pluralize controller names in blueprint routes.               *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  * For example, REST blueprints for `FooController` with `pluralize`        *
  * enabled:                                                                 *
  * GET /foos/:id?                                                           *
  * POST /foos                                                               *
  * PUT /foos/:id?                                                           *
  * DELETE /foos/:id?                                                        *
  *                                                                          *
  ***************************************************************************/

  // pluralize: false,

  /***************************************************************************
  *                                                                          *
  * Whether the blueprint controllers should populate model fetches with     *
  * data from other models which are linked by associations                  *
  *                                                                          *
  * If you have a lot of data in one-to-many associations, leaving this on   *
  * may result in very heavy api calls                                       *
  *                                                                          *
  ***************************************************************************/

  // populate: true,

  /****************************************************************************
  *                                                                           *
  * Whether to run Model.watch() in the find and findOne blueprint actions.   *
  * Can be overridden on a per-model basis.                                   *
  *                                                                           *
  ****************************************************************************/

  // autoWatch: true,

  /****************************************************************************
  *                                                                           *
  * The default number of records to show in the response from a "find"       *
  * action. Doubles as the default size of populated arrays if populate is    *
  * true.                                                                     *
  *                                                                           *
  ****************************************************************************/

  // defaultLimit: 30

};
<commit_msg>Turn off default routes<commit_after>/**
 * Blueprint API Configuration
 * (sails.config.blueprints)
 *
 * These settings are for the global configuration of blueprint routes and
 * request options (which impact the behavior of blueprint actions).
 *
 * You may also override any of these settings on a per-controller basis
 * by defining a '_config' key in your controller definition, and assigning it
 * a configuration object with overrides for the settings in this file.
 * A lot of the configuration options below affect so-called "CRUD methods",
 * or your controllers' `find`, `create`, `update`, and `destroy` actions.
 *
 * It's important to realize that, even if you haven't defined these yourself, as long as
 * a model exists with the same name as the controller, Sails will respond with built-in CRUD
 * logic in the form of a JSON API, including support for sort, pagination, and filtering.
 *
 * For more information on the blueprint API, check out:
 * http://sailsjs.org/#!/documentation/reference/blueprint-api
 *
 * For more information on the settings in this file, see:
 * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.blueprints.html
 *
 */

module.exports.blueprints = {

  /***************************************************************************
  *                                                                          *
  * Action routes speed up the backend development workflow by               *
  * eliminating the need to manually bind routes. When enabled, GET, POST,   *
  * PUT, and DELETE routes will be generated for every one of a controller's *
  * actions.                                                                 *
  *                                                                          *
  * If an `index` action exists, additional naked routes will be created for *
  * it. Finally, all `actions` blueprints support an optional path           *
  * parameter, `id`, for convenience.                                        *
  *                                                                          *
  * `actions` are enabled by default, and can be OK for production--         *
  * however, if you'd like to continue to use controller/action autorouting  *
  * in a production deployment, you must take great care not to              *
  * inadvertently expose unsafe/unintentional controller logic to GET        *
  * requests.                                                                *
  *                                                                          *
  ***************************************************************************/

  actions: false,

  /***************************************************************************
  *                                                                          *
  * RESTful routes (`sails.config.blueprints.rest`)                          *
  *                                                                          *
  * REST blueprints are the automatically generated routes Sails uses to     *
  * expose a conventional REST API on top of a controller's `find`,          *
  * `create`, `update`, and `destroy` actions.                               *
  *                                                                          *
  * For example, a BoatController with `rest` enabled generates the          *
  * following routes:                                                        *
  * :::::::::::::::::::::::::::::::::::::::::::::::::::::::                  *
  *  GET /boat -> BoatController.find                                        *
  *  GET /boat/:id -> BoatController.findOne                                 *
  *  POST /boat -> BoatController.create                                     *
  *  PUT /boat/:id -> BoatController.update                                  *
  *  DELETE /boat/:id -> BoatController.destroy                              *
  *                                                                          *
  * `rest` blueprint routes are enabled by default, and are suitable for use *
  * in a production scenario, as long you take standard security precautions *
  * (combine w/ policies, etc.)                                              *
  *                                                                          *
  ***************************************************************************/

  rest: false,

  /***************************************************************************
  *                                                                          *
  * Shortcut routes are simple helpers to provide access to a                *
  * controller's CRUD methods from your browser's URL bar. When enabled,     *
  * GET, POST, PUT, and DELETE routes will be generated for the              *
  * controller's`find`, `create`, `update`, and `destroy` actions.           *
  *                                                                          *
  * `shortcuts` are enabled by default, but should be disabled in            *
  * production.                                                              *
  *                                                                          *
  ***************************************************************************/

  shortcuts: false,

  /***************************************************************************
  *                                                                          *
  * An optional mount path for all blueprint routes on a controller,         *
  * including `rest`, `actions`, and `shortcuts`. This allows you to take    *
  * advantage of blueprint routing, even if you need to namespace your API   *
  * methods.                                                                 *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  ***************************************************************************/

  // prefix: '',

  /***************************************************************************
   *                                                                          *
   * An optional mount path for all REST blueprint routes on a controller.    *
   * And it do not include `actions` and `shortcuts` routes.                  *
   * This allows you to take advantage of REST blueprint routing,             *
   * even if you need to namespace your RESTful API methods                   *
   *                                                                          *
   ***************************************************************************/

  // restPrefix: '',

  /***************************************************************************
  *                                                                          *
  * Whether to pluralize controller names in blueprint routes.               *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  * For example, REST blueprints for `FooController` with `pluralize`        *
  * enabled:                                                                 *
  * GET /foos/:id?                                                           *
  * POST /foos                                                               *
  * PUT /foos/:id?                                                           *
  * DELETE /foos/:id?                                                        *
  *                                                                          *
  ***************************************************************************/

  // pluralize: false,

  /***************************************************************************
  *                                                                          *
  * Whether the blueprint controllers should populate model fetches with     *
  * data from other models which are linked by associations                  *
  *                                                                          *
  * If you have a lot of data in one-to-many associations, leaving this on   *
  * may result in very heavy api calls                                       *
  *                                                                          *
  ***************************************************************************/

  // populate: true,

  /****************************************************************************
  *                                                                           *
  * Whether to run Model.watch() in the find and findOne blueprint actions.   *
  * Can be overridden on a per-model basis.                                   *
  *                                                                           *
  ****************************************************************************/

  // autoWatch: true,

  /****************************************************************************
  *                                                                           *
  * The default number of records to show in the response from a "find"       *
  * action. Doubles as the default size of populated arrays if populate is    *
  * true.                                                                     *
  *                                                                           *
  ****************************************************************************/

  // defaultLimit: 30

};
<|endoftext|>


<commit_before>'use strict';

import {RedocComponent, BaseComponent} from '../base';
import SchemaManager from '../../utils/SchemaManager';
import {NgZone} from 'angular2/angular2';
import {redocEvents} from '../../events';

const CHANGE = {
  NEXT : 1,
  BACK : -1,
  INITIAL : 0
};

const INVIEW_POSITION = {
  ABOVE : 1,
  BELLOW: -1,
  INVIEW: 0
};

@RedocComponent({
  selector: 'side-menu',
  providers: [SchemaManager],
  templateUrl: './lib/components/SideMenu/side-menu.html',
  styleUrls: ['./lib/components/SideMenu/side-menu.css']
})
export default class SideMenu extends BaseComponent {
  constructor(schemaMgr, zone) {
    super(schemaMgr);
    this.zone = zone;

    // for some reason constructor is not run inside zone
    // as workaround running it manually
    this.zone.run(() => {
      this.bindScroll();
    });
    this.activeCatIdx = 0;
    this.activeMethodIdx = -1;
    this.prevOffsetY = null;

    redocEvents.bootstrapped.subscribe(() => this.hashScroll());
  }

  hashScroll() {
    let hash = window.location.hash;
    if (!hash) return;

    hash = hash.substr(1);
    let tag = hash.split('/')[0];
    let ptr = hash.substr(tag.length);
    console.log(tag, ptr);
    let el = this.getMethodEl(ptr, tag);
    if (el) this.scrollTo(el);
  }

  bindScroll() {
    this.prevOffsetY = window.scrollY;
    let view = document.querySelector('redoc');
    this.viewBoxTop = view.offsetTop;
    window.addEventListener('scroll', () => this.scrollHandler());
  }

  activateAndScroll(idx, methodIdx) {
    this.activate(idx, methodIdx);
    this.scrollToActive();
  }

  scrollTo(el) {
    let subjRect = el.getBoundingClientRect();
    let offset = window.scrollY + subjRect.top - this.viewBoxTop;
    window.scrollTo(0, offset);
  }

  scrollToActive() {
    this.scrollTo(this.getCurrentMethodEl());
  }

  activate(catIdx, methodIdx) {
    let menu = this.data.menu;
    menu[this.activeCatIdx].active = false;
    if (menu[this.activeCatIdx].methods.length) {
      if (this.activeMethodIdx >= 0) {
        menu[this.activeCatIdx].methods[this.activeMethodIdx].active = false;
      }
   }

    this.activeCatIdx = catIdx;
    this.activeMethodIdx = methodIdx;
    menu[catIdx].active = true;
    this.activeMethodPtr = null;
    if (menu[catIdx].methods.length && (methodIdx > -1)) {
      let currentItem = menu[catIdx].methods[methodIdx];
      currentItem.active = true;
      this.activeMethodPtr = currentItem.pointer;
    }
  }

  _calcActiveIndexes(offset) {
    let menu = this.data.menu;
    let catCount = menu.length;
    let catLength = menu[this.activeCatIdx].methods.length;

    let resMethodIdx = this.activeMethodIdx + offset;
    let resCatIdx = this.activeCatIdx;

    if (resMethodIdx > catLength - 1) {
      resCatIdx++;
      resMethodIdx = -1;
    }
    if (resMethodIdx < -1) {
      let prevCatIdx = --resCatIdx;
      catLength = menu[Math.max(prevCatIdx, 0)].methods.length;
      resMethodIdx = catLength - 1;
    }
    if (resCatIdx > catCount - 1) {
      resCatIdx = catCount - 1;
      resMethodIdx = catLength - 1;
    }
    if (resCatIdx < 0) {
      resCatIdx = 0;
      resMethodIdx = 0;
    }

    return [resCatIdx, resMethodIdx];
  }

  changeActive(offset = 1) {
    let [catIdx, methodIdx] = this._calcActiveIndexes(offset);
    this.activate(catIdx, methodIdx);
    return (methodIdx === 0 && catIdx === 0);
  }

  getMethodEl(ptr, tag) {
    let selector = ptr ? `[pointer="${ptr}"][tag="${tag}"]` : `[tag="${tag}"]`;
    return document.querySelector(selector);
  }

  getCurrentMethodEl() {
    return this.getMethodEl(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name);
  }

  /* returns 1 if element if above the view, 0 if in view and -1 below the view */
  getElementInViewPos(el) {
    if (Math.floor(el.getBoundingClientRect().top) > this.viewBoxTop) {
      return INVIEW_POSITION.ABOVE;
    }

    if (el.getBoundingClientRect().bottom <= this.viewBoxTop) {
      return INVIEW_POSITION.BELLOW;
    }
    return INVIEW_POSITION.INVIEW;
  }

  scrollHandler() {
    let isScrolledDown = (window.scrollY - this.prevOffsetY > 0);
    this.prevOffsetY = window.scrollY;
    let stable = false;
    while(!stable) {
      let activeMethodHost = this.getCurrentMethodEl();
      if (!activeMethodHost) return;
      var elementInViewPos = this.getElementInViewPos(activeMethodHost);
      if(isScrolledDown && elementInViewPos === INVIEW_POSITION.BELLOW) {
        stable = this.changeActive(CHANGE.NEXT);
        continue;
      }
      if(!isScrolledDown && elementInViewPos === INVIEW_POSITION.ABOVE ) {
        stable = this.changeActive(CHANGE.BACK);
        continue;
      }
      stable = true;
    }
  }

  prepareModel() {
    this.data = {};
    this.data.menu = Array.from(this.schemaMgr.buildMenuTree().entries()).map(
      el => ({name: el[0], description: el[1].description, methods: el[1].methods})
    );
  }

  init() {
    this.changeActive(CHANGE.INITIAL);
  }
}
SideMenu.parameters = SideMenu.parameters.concat([NgZone]);
<commit_msg>Fix minor hash scroll menu sync issue<commit_after>'use strict';

import {RedocComponent, BaseComponent} from '../base';
import SchemaManager from '../../utils/SchemaManager';
import {NgZone} from 'angular2/angular2';
import {redocEvents} from '../../events';

const CHANGE = {
  NEXT : 1,
  BACK : -1,
  INITIAL : 0
};

const INVIEW_POSITION = {
  ABOVE : 1,
  BELLOW: -1,
  INVIEW: 0
};

@RedocComponent({
  selector: 'side-menu',
  providers: [SchemaManager],
  templateUrl: './lib/components/SideMenu/side-menu.html',
  styleUrls: ['./lib/components/SideMenu/side-menu.css']
})
export default class SideMenu extends BaseComponent {
  constructor(schemaMgr, zone) {
    super(schemaMgr);
    this.zone = zone;

    // for some reason constructor is not run inside zone
    // as workaround running it manually
    this.zone.run(() => {
      this.bindScroll();
    });
    this.activeCatIdx = 0;
    this.activeMethodIdx = -1;
    this.prevOffsetY = null;

    redocEvents.bootstrapped.subscribe(() => this.hashScroll());
  }

  hashScroll() {
    let hash = window.location.hash;
    if (!hash) return;

    hash = hash.substr(1);
    let tag = hash.split('/')[0];
    let ptr = hash.substr(tag.length);
    console.log(tag, ptr);
    let el = this.getMethodEl(ptr, tag);
    if (el) this.scrollTo(el);
  }

  bindScroll() {
    this.prevOffsetY = window.scrollY;
    let view = document.querySelector('redoc');
    this.viewBoxTop = view.offsetTop;
    window.addEventListener('scroll', () => this.scrollHandler());
  }

  activateAndScroll(idx, methodIdx) {
    this.activate(idx, methodIdx);
    this.scrollToActive();
  }

  scrollTo(el) {
    let subjRect = el.getBoundingClientRect();
    let offset = window.scrollY + subjRect.top - this.viewBoxTop + 1;
    window.scrollTo(0, offset);
  }

  scrollToActive() {
    this.scrollTo(this.getCurrentMethodEl());
  }

  activate(catIdx, methodIdx) {
    let menu = this.data.menu;
    menu[this.activeCatIdx].active = false;
    if (menu[this.activeCatIdx].methods.length) {
      if (this.activeMethodIdx >= 0) {
        menu[this.activeCatIdx].methods[this.activeMethodIdx].active = false;
      }
   }

    this.activeCatIdx = catIdx;
    this.activeMethodIdx = methodIdx;
    menu[catIdx].active = true;
    this.activeMethodPtr = null;
    if (menu[catIdx].methods.length && (methodIdx > -1)) {
      let currentItem = menu[catIdx].methods[methodIdx];
      currentItem.active = true;
      this.activeMethodPtr = currentItem.pointer;
    }
  }

  _calcActiveIndexes(offset) {
    let menu = this.data.menu;
    let catCount = menu.length;
    let catLength = menu[this.activeCatIdx].methods.length;

    let resMethodIdx = this.activeMethodIdx + offset;
    let resCatIdx = this.activeCatIdx;

    if (resMethodIdx > catLength - 1) {
      resCatIdx++;
      resMethodIdx = -1;
    }
    if (resMethodIdx < -1) {
      let prevCatIdx = --resCatIdx;
      catLength = menu[Math.max(prevCatIdx, 0)].methods.length;
      resMethodIdx = catLength - 1;
    }
    if (resCatIdx > catCount - 1) {
      resCatIdx = catCount - 1;
      resMethodIdx = catLength - 1;
    }
    if (resCatIdx < 0) {
      resCatIdx = 0;
      resMethodIdx = 0;
    }

    return [resCatIdx, resMethodIdx];
  }

  changeActive(offset = 1) {
    let [catIdx, methodIdx] = this._calcActiveIndexes(offset);
    this.activate(catIdx, methodIdx);
    return (methodIdx === 0 && catIdx === 0);
  }

  getMethodEl(ptr, tag) {
    let selector = ptr ? `[pointer="${ptr}"][tag="${tag}"]` : `[tag="${tag}"]`;
    return document.querySelector(selector);
  }

  getCurrentMethodEl() {
    return this.getMethodEl(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name);
  }

  /* returns 1 if element if above the view, 0 if in view and -1 below the view */
  getElementInViewPos(el) {
    if (Math.floor(el.getBoundingClientRect().top) > this.viewBoxTop) {
      return INVIEW_POSITION.ABOVE;
    }

    if (el.getBoundingClientRect().bottom <= this.viewBoxTop) {
      return INVIEW_POSITION.BELLOW;
    }
    return INVIEW_POSITION.INVIEW;
  }

  scrollHandler() {
    let isScrolledDown = (window.scrollY - this.prevOffsetY > 0);
    this.prevOffsetY = window.scrollY;
    let stable = false;
    while(!stable) {
      let activeMethodHost = this.getCurrentMethodEl();
      if (!activeMethodHost) return;
      var elementInViewPos = this.getElementInViewPos(activeMethodHost);
      if(isScrolledDown && elementInViewPos === INVIEW_POSITION.BELLOW) {
        stable = this.changeActive(CHANGE.NEXT);
        continue;
      }
      if(!isScrolledDown && elementInViewPos === INVIEW_POSITION.ABOVE ) {
        stable = this.changeActive(CHANGE.BACK);
        continue;
      }
      stable = true;
    }
  }

  prepareModel() {
    this.data = {};
    this.data.menu = Array.from(this.schemaMgr.buildMenuTree().entries()).map(
      el => ({name: el[0], description: el[1].description, methods: el[1].methods})
    );
  }

  init() {
    this.changeActive(CHANGE.INITIAL);
  }
}
SideMenu.parameters = SideMenu.parameters.concat([NgZone]);
<|endoftext|>


<commit_before>'use strict';

var Promise = require('bluebird'),
    mongoose = require('mongoose');

module.exports.setAclEntry = (resource, user, action) => {
    if (!Array.isArray(user)) {
        user = [user];
    }
    if (!Array.isArray(action)) {
        action = [action];
    }
    return mongoose.model('AclEntry').findOne({resource_key: resource, user_key: user})
        .then(function (entry) {
            if (!entry) {
                entry = {
                    resource_key: resource,
                    user_key: user,
                    actions: []
                };
            }
            for (var i = 0; i < action.length; i += 1) {
                if (entry.actions.indexOf(action[i]) < 0) {
                    entry.actions.push(action[i]);
                }
            }
            return mongoose.model('AclEntry').findOneAndUpdate({
                resource_key: resource,
                user_key: user
            }, entry, {upsert:true});
        });
};

module.exports.removeAclEntry = (resource, user, action) => {
    return mongoose.model('AclEntry').findOne({resource_key: resource, user_key: user})
        .then(function (entry) {
            if (entry) {
                if (action === '*') {
                    entry.actions = [];
                } else {
                    if (entry.actions.indexOf(action) > -1) {
                        entry.actions = entry.actions.splice(entry.actions.indexOf(action), 1);
                    }
                }
                if (entry.actions.length === 0) {
                    return mongoose.model('AclEntry').findOneAndRemove({resource_key: resource, user_key: user})
                } else {
                    return entry.save();
                }
            }
        });
};

module.exports.isAllowed = (resource, user, action) => {
    return mongoose.model('AclEntry').findOne({resource_key: resource, user_key: user})
        .then(function (entry) {
            if (!entry) {
                return false;
            }
            return entry.actions.indexOf(action) > -1;
        });
};<commit_msg>acl fix<commit_after>'use strict';

var Promise = require('bluebird'),
    mongoose = require('mongoose');

module.exports.setAclEntry = (resource, user, action) => {
    if (!Array.isArray(user)) {
        user = [user];
    }
    if (!Array.isArray(action)) {
        action = [action];
    }
    return mongoose.model('AclEntry').findOne({resource_key: resource, role_key: user})
        .then(function (entry) {
            if (!entry) {
                entry = {
                    resource_key: resource,
                    role_key: user,
                    actions: []
                };
            }
            for (var i = 0; i < action.length; i += 1) {
                if (entry.actions.indexOf(action[i]) < 0) {
                    entry.actions.push(action[i]);
                }
            }
            return mongoose.model('AclEntry').findOneAndUpdate({
                resource_key: resource,
                role_key: user
            }, entry, {upsert:true});
        });
};

module.exports.removeAclEntry = (resource, user, action) => {
    return mongoose.model('AclEntry').findOne({resource_key: resource, role_key: user})
        .then(function (entry) {
            if (entry) {
                if (action === '*') {
                    entry.actions = [];
                } else {
                    if (entry.actions.indexOf(action) > -1) {
                        entry.actions = entry.actions.splice(entry.actions.indexOf(action), 1);
                    }
                }
                if (entry.actions.length === 0) {
                    return mongoose.model('AclEntry').findOneAndRemove({resource_key: resource, role_key: user})
                } else {
                    return entry.save();
                }
            }
        });
};

module.exports.isAllowed = (resource, user, action) => {
    return mongoose.model('AclEntry').findOne({resource_key: resource, role_key: user})
        .then(function (entry) {
            if (!entry) {
                return false;
            }
            return entry.actions.indexOf(action) > -1;
        });
};<|endoftext|>


<commit_before>import { planParamsToQuery } from '@opentripplanner/core-utils/lib/query'
import { getTimeFormat, OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time'
import moment from 'moment'
import React, { Component } from 'react'
import { connect } from 'react-redux'

import * as formActions from '../../actions/form'
import {CallRecordButton, CallRecordIcon} from './styled'

/**
 * Displays information for a query stored for the Call Taker module.
 */
class QueryRecordLayout extends Component {
  _getParams = () => {
    const {query} = this.props
    return planParamsToQuery(JSON.parse(query.queryParams))
  }

  _viewQuery = () => {
    const {parseUrlQueryString} = this.props
    const params = this._getParams()
    params.departArrive = params.arriveBy ? 'ARRIVE' : 'DEPART'
    parseUrlQueryString(params, '_CALL')
  }

  render () {
    const {query, timeFormat} = this.props
    const params = this._getParams()
    const time = query.timeStamp
      ? moment(query.timeStamp).format(timeFormat)
      : moment(params.time, OTP_API_TIME_FORMAT).format(timeFormat)
    return (

        <CallRecordButton onClick={this._viewQuery} className='clear-button-formatting'>
          <CallRecordIcon type='search' />
          {time}

          {params.from.name} to {params.to.name}
        </CallRecordButton>

    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    config: state.otp.config,
    timeFormat: getTimeFormat(state.otp.config)
  }
}

const {parseUrlQueryString} = formActions

const mapDispatchToProps = { parseUrlQueryString }

const QueryRecord = connect(mapStateToProps, mapDispatchToProps)(QueryRecordLayout)
export default QueryRecord
<commit_msg>refactor(query-record): remove unused config prop<commit_after>import { planParamsToQuery } from '@opentripplanner/core-utils/lib/query'
import { getTimeFormat, OTP_API_TIME_FORMAT } from '@opentripplanner/core-utils/lib/time'
import moment from 'moment'
import React, { Component } from 'react'
import { connect } from 'react-redux'

import * as formActions from '../../actions/form'
import {CallRecordButton, CallRecordIcon} from './styled'

/**
 * Displays information for a query stored for the Call Taker module.
 */
class QueryRecordLayout extends Component {
  _getParams = () => {
    const {query} = this.props
    return planParamsToQuery(JSON.parse(query.queryParams))
  }

  _viewQuery = () => {
    const {parseUrlQueryString} = this.props
    const params = this._getParams()
    params.departArrive = params.arriveBy ? 'ARRIVE' : 'DEPART'
    parseUrlQueryString(params, '_CALL')
  }

  render () {
    const {query, timeFormat} = this.props
    const params = this._getParams()
    const time = query.timeStamp
      ? moment(query.timeStamp).format(timeFormat)
      : moment(params.time, OTP_API_TIME_FORMAT).format(timeFormat)
    return (

        <CallRecordButton onClick={this._viewQuery} className='clear-button-formatting'>
          <CallRecordIcon type='search' />
          {time}

          {params.from.name} to {params.to.name}
        </CallRecordButton>

    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    timeFormat: getTimeFormat(state.otp.config)
  }
}

const {parseUrlQueryString} = formActions

const mapDispatchToProps = { parseUrlQueryString }

const QueryRecord = connect(mapStateToProps, mapDispatchToProps)(QueryRecordLayout)
export default QueryRecord
<|endoftext|>


<commit_before>/**
 * 基于百度OCR的身份证识别
 * @authors Dophin (940101379@qq.com)
 * @date    2015-07-07 21:22:49
 * @version 1.0.0
 */

//配置
var OBVERSE = 'obverse';
var REVERSE = 'reverse';
var UNKNOWN_SIDE_MSG = '未识别出正反面';
var OBVERSE_SIDE_MSG = '识别成功，为正面';
var REVERSE_SIDE_MSG = '识别成功，为反面';

function IDCardOCR(apikey) {
    this.apikey = apikey;
}

/**
 * 扫描身份证，返回格式化数据，可在回调函数中处理
 * @param  {string}   idcard 身份证图片的路径
 * @param  {string}   side   身份证的正反面，正面为'obverse'，反面为'reverse'，除此之外皆看做自动识别正反面
 * @param  {Function} cb     回调函数，可对扫描结果进行进一步处理
 * @return {undefined}
 */
IDCardOCR.prototype.scan = function(idcard, side, cb) {

    if (typeof cb !== 'function') { //容错处理
        cb = function() {}
    }

    var ocr = require('baidu-ocr').create(this.apikey);

    // detectType: `LocateRecognize`代表整图文字检测、识别,以行为单位（默认）
    // languageType: `CHN_ENG`(中英)（默认）
    // imageType: `2`代表图片原文件（只支持JPG，大小不能超过300K）
    // image: 图片路径
    ocr.scan('LocateRecognize', 'CHN_ENG', 2, idcard, function(err, data) {
        if (err) {
            cb(retObj(-1, err.errMsg), null);
            return;
        }

        var word = Trim(data.word); //去掉字符串中的空白符
        word = deleteSpeChar(word); //去掉字符串中的特殊字符

        if (isSideValWrong(side)) { //自动识别
            side = recognizeSide(word);
            if (side === 'unknown') { //未识别出正反面
                cb(retObj(-1, UNKNOWN_SIDE_MSG), null);
                return;
            }
        }

        var retData = {};
        if (side === OBVERSE) { //正面
            retData.name = extractName(word);
            retData.sex = extractSex(word);
            retData.nation = extractNation(word);
            retData.birthday = extractBirthday(word);
            retData.residence = extractResidence(word);
            retData.idNum = extractIdNum(word);
            retData.side = 'obverse';
            cb(null, retObj(0, OBVERSE_SIDE_MSG, retData));
        } else { //反面
            retData.authority = extractAuthority(word);
            retData.validPeriod = extractValidPeriod(word);
            retData.side = 'reverse';
            cb(null, retObj(0, REVERSE_SIDE_MSG, retData));
        }
    });

}

exports.create = function(apikey) {
    return new IDCardOCR(apikey);
}

if (process.env.IN_TEST) {
    exports.extractValidPeriod = extractValidPeriod;
    exports.extractBirthday = extractBirthday;
}

/*================辅助函数================*/
function extractName(s) {
    var reg = /姓名(.*)性别/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractSex(s) {
    var reg = /性别(.*)民族/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractNation(s) {
    var reg = /民族(.*)出生/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractBirthday(s) {
    var reg = /出生(.*)住址/;
    var result = reg.exec(s);
    if (!result)
        return '';
    var year,month,day;
    year = extractYear(result[1]);  //result[1]可能为空字符串
    month = extractMonth(result[1]);
    day = extractDay(result[1]);

    return year+'-'+month+'-'+day;

    function extractYear(s){
        var arr = /(\d*)年/.exec(s);
        if(!arr)
            return 'xxxx';
        if(isNaN(parseInt(arr[1]))) //如果不是数值
            return 'xxxx';
        return arr[1];
    }
    function extractMonth(s){
        var arr = /年(\d*)月/.exec(s);
        if(!arr)
            return 'xx';
        if(isNaN(parseInt(arr[1]))) {//如果不是数值
            return 'xx';
        }else{  //是数值
            if(parseInt(arr[1])<10)
                return '0'+arr[1];
        }
        return arr[1];
    }
    function extractDay(s){
        var arr = /月(\d*)日/.exec(s);
        if(!arr)
            return 'xx';
        if(isNaN(parseInt(arr[1]))) {//如果不是数值
            return 'xx';
        }else{  //是数值
            if(parseInt(arr[1])<10)
                return '0'+arr[1];
        }
        return arr[1];
    }
}

function extractResidence(s) {
    var reg = /住址(.*)公民身份/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractIdNum(s) {
    var reg = /身份号码(.*)/;
    var result = reg.exec(s);
    if (!result)
        return '';
    reg = /\d/g;
    return result[1].match(reg).join('');
}

function extractAuthority(s) {
    var reg = /签发机关(.*)有效期限/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractValidPeriod(s) {
    var reg = /有效期限(.*)/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return dateFormat(result[1]); //

    function dateFormat(s) {
        var reg = /\d/g;
        s = s.match(reg).join(''); //取出数字部分
        if (s.length !== 16) {
            return s;
        }
        var start = s.substr(0, 8);
        var end = s.substr(8, 8);
        return (format(start) + '~' + format(end));

        function format(s) {
            var arr = s.split('');
            arr.splice(4, 0, '-');
            arr.splice(7, 0, '-');
            return arr.join('');
        }
    }
}

function retObj(errNum, errMsg, retData) {
    if (!retData)
        retData = '';
    return {
        errNum: errNum,
        errMsg: errMsg,
        retData: retData
    }
}

//识别正反面
function recognizeSide(s) {
    var obverse_arr = ['姓名', '性别', '民族', '出生', '住址', '号码']; //正面标识
    var reverse_arr = ['共和国', '居民身份证', '签发机关', '有效期限']; //反面标识
    var obverse_flag, reverse_flag;
    obverse_flag = obverse_arr.some(function(v) {
        if (s.indexOf(v) === -1) { //没找到
            return false;
        } else {
            return true;
        }
    });
    if (obverse_flag === true) { //如果为正面
        return OBVERSE;
    }

    reverse_flag = reverse_arr.some(function(v) {
        if (s.indexOf(v) === -1) { //没找到
            return false;
        } else {
            return true;
        }
    });
    if (reverse_flag === true) { //如果为反面
        return REVERSE;
    }
    return 'unknown';
}

function isSideValWrong(s) {
    if (s !== OBVERSE && s !== REVERSE) {
        return true;
    } else {
        return false;
    }
}

//去除字符串中的所有空白符
function Trim(s) {
    return s.replace(/\s/g, '');
}

//祛除字符串中的所有特殊字符
function deleteSpeChar(s){
    var set = '：·`:,.。‘~*\-+=%$#@!;、&%．';
    var reg = new RegExp('['+set+']','g');
    return s.replace(reg,'');
}
<commit_msg>更改版本号<commit_after>/**
 * 基于百度OCR的身份证识别
 * @authors Dophin (940101379@qq.com)
 * @date    2015-07-07 21:22:49
 * @version 1.0.5
 */

//配置
var OBVERSE = 'obverse';
var REVERSE = 'reverse';
var UNKNOWN_SIDE_MSG = '未识别出正反面';
var OBVERSE_SIDE_MSG = '识别成功，为正面';
var REVERSE_SIDE_MSG = '识别成功，为反面';

function IDCardOCR(apikey) {
    this.apikey = apikey;
}

/**
 * 扫描身份证，返回格式化数据，可在回调函数中处理
 * @param  {string}   idcard 身份证图片的路径
 * @param  {string}   side   身份证的正反面，正面为'obverse'，反面为'reverse'，除此之外皆看做自动识别正反面
 * @param  {Function} cb     回调函数，可对扫描结果进行进一步处理
 * @return {undefined}
 */
IDCardOCR.prototype.scan = function(idcard, side, cb) {

    if (typeof cb !== 'function') { //容错处理
        cb = function() {}
    }

    var ocr = require('baidu-ocr').create(this.apikey);

    // detectType: `LocateRecognize`代表整图文字检测、识别,以行为单位（默认）
    // languageType: `CHN_ENG`(中英)（默认）
    // imageType: `2`代表图片原文件（只支持JPG，大小不能超过300K）
    // image: 图片路径
    ocr.scan('LocateRecognize', 'CHN_ENG', 2, idcard, function(err, data) {
        if (err) {
            cb(retObj(-1, err.errMsg), null);
            return;
        }

        var word = Trim(data.word); //去掉字符串中的空白符
        word = deleteSpeChar(word); //去掉字符串中的特殊字符

        if (isSideValWrong(side)) { //自动识别
            side = recognizeSide(word);
            if (side === 'unknown') { //未识别出正反面
                cb(retObj(-1, UNKNOWN_SIDE_MSG), null);
                return;
            }
        }

        var retData = {};
        if (side === OBVERSE) { //正面
            retData.name = extractName(word);
            retData.sex = extractSex(word);
            retData.nation = extractNation(word);
            retData.birthday = extractBirthday(word);
            retData.residence = extractResidence(word);
            retData.idNum = extractIdNum(word);
            retData.side = 'obverse';
            cb(null, retObj(0, OBVERSE_SIDE_MSG, retData));
        } else { //反面
            retData.authority = extractAuthority(word);
            retData.validPeriod = extractValidPeriod(word);
            retData.side = 'reverse';
            cb(null, retObj(0, REVERSE_SIDE_MSG, retData));
        }
    });

}

exports.create = function(apikey) {
    return new IDCardOCR(apikey);
}

if (process.env.IN_TEST) {
    exports.extractValidPeriod = extractValidPeriod;
    exports.extractBirthday = extractBirthday;
}

/*================辅助函数================*/
function extractName(s) {
    var reg = /姓名(.*)性别/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractSex(s) {
    var reg = /性别(.*)民族/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractNation(s) {
    var reg = /民族(.*)出生/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractBirthday(s) {
    var reg = /出生(.*)住址/;
    var result = reg.exec(s);
    if (!result)
        return '';
    var year,month,day;
    year = extractYear(result[1]);  //result[1]可能为空字符串
    month = extractMonth(result[1]);
    day = extractDay(result[1]);

    return year+'-'+month+'-'+day;

    function extractYear(s){
        var arr = /(\d*)年/.exec(s);
        if(!arr)
            return 'xxxx';
        if(isNaN(parseInt(arr[1]))) //如果不是数值
            return 'xxxx';
        return arr[1];
    }
    function extractMonth(s){
        var arr = /年(\d*)月/.exec(s);
        if(!arr)
            return 'xx';
        if(isNaN(parseInt(arr[1]))) {//如果不是数值
            return 'xx';
        }else{  //是数值
            if(parseInt(arr[1])<10)
                return '0'+arr[1];
        }
        return arr[1];
    }
    function extractDay(s){
        var arr = /月(\d*)日/.exec(s);
        if(!arr)
            return 'xx';
        if(isNaN(parseInt(arr[1]))) {//如果不是数值
            return 'xx';
        }else{  //是数值
            if(parseInt(arr[1])<10)
                return '0'+arr[1];
        }
        return arr[1];
    }
}

function extractResidence(s) {
    var reg = /住址(.*)公民身份/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractIdNum(s) {
    var reg = /身份号码(.*)/;
    var result = reg.exec(s);
    if (!result)
        return '';
    reg = /\d/g;
    return result[1].match(reg).join('');
}

function extractAuthority(s) {
    var reg = /签发机关(.*)有效期限/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return result[1];
}

function extractValidPeriod(s) {
    var reg = /有效期限(.*)/;
    var result = reg.exec(s);
    if (!result)
        return '';
    return dateFormat(result[1]); //

    function dateFormat(s) {
        var reg = /\d/g;
        s = s.match(reg).join(''); //取出数字部分
        if (s.length !== 16) {
            return s;
        }
        var start = s.substr(0, 8);
        var end = s.substr(8, 8);
        return (format(start) + '~' + format(end));

        function format(s) {
            var arr = s.split('');
            arr.splice(4, 0, '-');
            arr.splice(7, 0, '-');
            return arr.join('');
        }
    }
}

function retObj(errNum, errMsg, retData) {
    if (!retData)
        retData = '';
    return {
        errNum: errNum,
        errMsg: errMsg,
        retData: retData
    }
}

//识别正反面
function recognizeSide(s) {
    var obverse_arr = ['姓名', '性别', '民族', '出生', '住址', '号码']; //正面标识
    var reverse_arr = ['共和国', '居民身份证', '签发机关', '有效期限']; //反面标识
    var obverse_flag, reverse_flag;
    obverse_flag = obverse_arr.some(function(v) {
        if (s.indexOf(v) === -1) { //没找到
            return false;
        } else {
            return true;
        }
    });
    if (obverse_flag === true) { //如果为正面
        return OBVERSE;
    }

    reverse_flag = reverse_arr.some(function(v) {
        if (s.indexOf(v) === -1) { //没找到
            return false;
        } else {
            return true;
        }
    });
    if (reverse_flag === true) { //如果为反面
        return REVERSE;
    }
    return 'unknown';
}

function isSideValWrong(s) {
    if (s !== OBVERSE && s !== REVERSE) {
        return true;
    } else {
        return false;
    }
}

//去除字符串中的所有空白符
function Trim(s) {
    return s.replace(/\s/g, '');
}

//祛除字符串中的所有特殊字符
function deleteSpeChar(s){
    var set = '：·`:,.。‘~*\-+=%$#@!;、&%．';
    var reg = new RegExp('['+set+']','g');
    return s.replace(reg,'');
}
<|endoftext|>