jquery.ext.js

/**
 * The jQuery plugins.
 * @author Bendy Zhang <zb@bndy.net>
 * @copyright BNDY.NET 2017
 * @see {@link http://bndy.net|Home Page}
 */

'use strict';

;(function ($) {
    //==================================================================
    /**
     * @external jQuery
     */
    $.extend({
        /**
         * Enables/disables window scrolling.
         * @function external:jQuery.toggleScroll
         * @example
         * $.toggleScroll();
         */
        toggleScroll: function () {
            $('body').toggleScroll();
        },

        /**
         * Gets a single Promise that resolves all $.ajax functions.
         * @function external:jQuery.ajaxAll
         * @param {jqXhr} argument - A jqXhr such as $.ajax({})
         * @param {jqXhr} ... - more
         * @returns {Promise} A single promise.
         * @example
         * $.ajaxAll($.get(...), $.post(...), $.ajax(...)).then(function(values){}, function(rejections){});
         * $.ajaxAll($.get(...));
         */
        ajaxAll: function () {
            var promises = [];
            for (var idx = 0; idx < arguments.length; idx++) {
                var jqXhr = arguments[idx];
                promises.push(new Promise(function (resolve, reject) {
                    jqXhr.done(resolve).fail(reject);
                }));
            }

            return Promise.all(promises);
        },
    });


    //==================================================================
    /**
     * @external "jQuery.fn"
     */
    $.fn.extend({

        /**
         * Highlights the text using HTML mark.
         * @function external:"jQuery.fn"#highlightText
         * @param {string} text - The text needs to highlight.
         * @returns {jQuery} The elements matched.
         * @example
         * <p>Hello World!</p>
         * $('p').highlightText('e');
         * // => <p>H<mark class="highlight">e</mark>llo World!</p>
         */
        highlightText: function (text) {
            $(this).each(function () {
                if ($(this).children().length === 0) {
                    $(this).html($(this).text().replace(
                        new RegExp('(' + text + ')', "ig"),
                        '<mark class="highlight">$1</mark>'
                    ));
                }
            });
            return $(this);
        },

        /**
         * Enables/disables element scrolling.
         * @function external:"jQuery.fn"#toggleScroll
         * @example
         * $('.box').toggleScroll();
         */
        toggleScroll: function () {
            if ($(this).css('overflow') === 'hidden') {
                $(this).css('overflow', 'auto');
            } else {
                $(this).css('overflow', 'hidden');
            }
        },

        /**
         * Sets a cover for current element.
         * @function external:"jQuery.fn"#cover
         * @param {string} msg - The cover html content.
         * @returns {jQuery} The cover element if msg is not false, otherwise nothing.
         * @example
         * var loading = $('.box').cover('Loading...');
         * loading.cover(false);    // remove cover
         */
        cover: function (msg) {
            var parent = $(this);
            if (parent.hasClass('loading-wrapper')) {
                parent = parent.parent();
            }

            if (msg || typeof msg === 'undefined') {
                parent.css({
                    position: 'relative'
                });
                var elem = $('<div class="loading-wrapper"><span style="display: table-cell;vertical-align: middle;text-align: center;"></span></div>');
                elem.find('span').html(msg);
                elem.css({
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    display: 'table',
                    width: parent.outerWidth(),
                    height: parent.outerHeight(),
                    textAlgin: 'center',
                });
                parent.append(elem);
                return elem;
            } else {
                parent.find('.loading-wrapper').remove();
            }
        },

        /**
         * Animates css for target.
         * @function external:"jQuery.fn"#animateCss
         * @param {string} animationName - The animation name in https://github.com/daneden/animate.css
         * @param {function} callback - The callback function.
         * @example
         * $('.box').animateCss('bounce', function() {});
         */
        animateCss: function (animationName, callback) {
            var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
            this.addClass('animated ' + animationName).one(animationEnd, function () {
                $(this).removeClass('animated ' + animationName);
                if (callback) {
                    callback();
                }
            });
            return this;
        },

        /**
         * Renders the html with specified data. 
         * Supports Handelbars engine or lodash for deeper level binding.
         * @function external:"jQuery.fn"#bindData
         * @requires {@link https://handlebarsjs.com/|handlebars} or {@link https://lodash.com/|lodash}
         * @param {object} data - The data model used to bind.
         * @param {targetId=} targetId - The id of target element which is used to include the html bound data.
         * @example
         * $('<h1>{{name}}</h1>').bindData({name: 'Bendy'});
         * // => '<h1>Bendy</h1>'
         * <script id="tpl" type="text/x-handlebars-template">
         *  <h1>{{name}}</h1>
         * </script>
         * <div id="container"></div>
         * $('#tpl').bindData({name: 'Bendy'}, 'container');
         * <div id="container"><h1>Bendy</h1></div>
         * @returns {string} The html string with bound data.
         */
        bindData: function(data, targetId) {
            var templateHtml = $(this).html();
            var html = templateHtml;
            if (typeof Handlebars !== 'undefined') {
                // Handlebars
                var compile = Handlebars.compile(templateHtml);
                html = compile(data);
            } else {
                if (typeof _ !== 'undefined') {
                    // use lodash to parse the variables
                    var binds = html.match(/{{[^}]+}}/g);
                    _.forEach(binds, function(bind) {
                        var key = bind.replace(/[{}]/g, '');
                        var val = _.get(data, key);
                        html = html.replace(new RegExp(bind.replace(/([\[\]\.])/g, '\\$1'), 'g'), val||'');
                    });
                } else {
                    var keys = Object.keys(data);
                    for (var idx = 0; idx < keys.length; idx++) {
                        var key = keys[idx];
                        html = html.replace('{{' + key + '}}', data[key]);
                    }
                }
                html = html.replace(/\{\{.+\}\}/g, '');
            }

            if (targetId) {
                $('#' + targetId).html(html);
            } else {
                $(this).html(html);
            }
            return html;
        },

        /**
         * Renders a Markdown Editor using editor.md.
         * @function external:"jQuery.fn"#mdEditor
         * @requires {@link https://pandao.github.io/editor.md/|editor.md}
         * @param {object} options - The options for editor.md. For example: { width: '50%', height: 300, path: '../node_modules/editor.md/lib/' }
         * @returns {object} An instance of editormd and attached `html()`, `markdown()` methods.
         * @example
         * <div id="mdEditorDiv"></div>
         * 
         * var editor = $('#mdEditorDiv').mdEditor({
         *      with: '50%',                                    // default 100%
         *      height: 400,                                    // default 640
         *      path: '../node_modules/editor.md/lib/'          // required, the path which includes all dependencies
         * });
         * editor.html();       // get html content
         * editor.markdown();   // get markdown content
         */
        mdEditor: function(options) {
            var id = $(this).attr('id');
            if (!id) {
                throw new Error('Not found element or No `id` defined for editor.md.');
            }
            if (!options.path) {
                throw new Error('Please specify `path` value for editor.md dependencies folder in `options`. `options` example: {path: `../node_modules/editor.md/lib/`}');
            }
            if (!editormd) {
                throw new Error('Requires editor.md (http://pandao.github.io/editor.md/)');
            }

            var defaultOptions = {
                width   : "100%",
                height  : 640,
                syncScrolling : "single",
                saveHTMLToTextarea : true,
            };
            options = $.extend({}, defaultOptions, options);
            var editor = editormd(id, options); 
            return $.extend(editor, {
                originHtml: editor.getHTML,
                markdown: editor.getMarkdown,
                html: editor.getPreviewedHTML,
            });
        },
    });
})(jQuery);