All Downloads are FREE. Search and download functionalities are using the official Maven repository.

a.hyperscala-realtime_2.11.0.10.3.source-code.realtime.js Maven / Gradle / Ivy

The newest version!
var realtime = {
    /**
     * The Communicate instance. Created on call to 'init'.
     */
    communicate: null,
    /**
     * Message / Event listeners object.
     */
    listeners: {},
    /**
     * Debug mode logs enabled logs additional information to the console in the browser.
     */
    debug: false,
    /**
     * Called by Realtime to initialize the communication connection.
     *
     * @param settings the settings information for Realtime and Communicate
     */
    init: function(settings) {
        try {
            this.debug = settings.debug;

            this.communicate = new Communicate(settings);
            this.communicate.on('json', function (obj) {
                if (obj.type != null) {
                    realtime.fire(obj.type, obj);
                    realtime.fire('*', obj);
                    //r.log('Received JSON', obj);
                }
            });
            this.communicate.on('init', function (evt) {
                realtime.send({                         // Initialize the connection with Realtime
                    type: 'init',
                    pageId: settings.pageId,
                    url: document.location.href
                });
            });
            this.communicate.on('open', function (evt) {
                realtime.fire('open', evt);
                console.log('WebSocket Connection opened.');
            });
            this.communicate.on('close', function (evt) {
                realtime.fire('close', evt);
                console.log('WebSocket Connection closed.');
            });
            this.communicate.on('error', function (evt) {
                realtime.fire('error', evt);
                realtime.log('Error occurred.', evt.data);
            });
            this.communicate.connect();
        } catch(err) {
            realtime.error('Failed to init', settings, err);
        }
    },
    /**
     * Sends a message to the server. Non-String values are stringified via JSON.stringify before sending.
     *
     * @param message the message to send
     */
    send: function(message) {
        realtime.log('Sending message.', message);
        if (this.communicate == null) {
            realtime.error('Unable to send message, Realtime has not yet initialized!', message);
        } else {
            this.communicate.send(message);
        }
    },
    limited: {},
    /**
     * Uses send but limits the frequency of sends to a maximum rate for the specified key. Additional calls to this for
     * the same key within the maximum rate are delayed and only send the latest message.
     *
     * @param key unique identifier for the group
     * @param message the current message to send
     * @param maximumRate the maximum rate of sending in milliseconds
     */
    sendLimited: function(key, message, maximumRate) {
        var limit = this.limited[key];
        var now = Date.now();
        if (limit == null || (limit.message == null && limit.lastSend <= now - maximumRate)) {     // Send immediately
            this.send(message);
            this.limited[key] = {
                lastSend: now
            }
        } else if (limit.message != null) {                                                        // Waiting to send, update message
            limit.message = message;
        } else {                                                                                   // Delay sending
            var delay = (limit.lastSend + maximumRate) - now;
            limit.message = message;
            setTimeout(function() {
                realtime.send(limit.message);
                limit.message = null;
                limit.lastSend = Date.now();
            }, delay);
        }
    },
    /**
     * Fire an event to the listeners.
     *
     * @param type the listeners group type
     * @param obj the event that is actually fired
     */
    fire: function(type, obj) {
        try {
            if (this.listeners[type] && this.listeners[type].length > 0) {
                for (var i = 0; i < this.listeners[type].length; i++) {
                    this.listeners[type][i](obj);
                }
            } else if ('open, close, error, *'.indexOf(type) == -1) {
                realtime.error('Nothing listening for events of type [' + type + '].', obj);
            }
        } catch(err) {
            realtime.error('Failed to fire event ' + type + ' to listeners because ' + err.message, obj, err);
        }
    },
    /**
     * Add a listener to a specific type of event.
     *
     * @param type the listeners group type
     * @param handler the function handler for events
     */
    on: function(type, handler) {
        if (this.listeners[type] == null) {
            this.listeners[type] = [];
        }
        this.listeners[type].push(handler);
    },
    /**
     * Used for debug logging. Calls to this function are ignored if 'debug' is disabled.
     *
     * @param message the message to log
     * @param obj optional object that is stringified on the end if provided
     */
    log: function(message, obj) {
        if (this.debug) {
            console.log('Realtime (' + new Date() + '): ' + message + (obj != null ? ' - ' + JSON.stringify(obj) : ''));
        }
    },
    error: function(message, obj, err) {
        console.log('Realtime (' + new Date() + '): ' + message + (obj != null ? ' - ' + JSON.stringify(obj) : ''));
        if (err != null) console.log('Stack Trace: ' + err.stack);
        this.send({
            type: 'browserError',
            timestamp: Date.now(),
            message: message,
            obj: JSON.stringify(obj),
            errorMessage: err != null ? err.message : null,
            stackTrace: err != null ? err.stack : null,
            url: document.location.href
        });
    },
    event: function(evt, confirmMessage, preventDefault, fireChange, delay, maximumRate) {
        try {
            if (evt.src) evt.target = evt.srcElement;       // Handling for older versions of IE

            var element = evt.currentTarget;                // Current target is where the listener was added, target is what fired it
            if (element == null) {
                element = evt.target;
            }
            if (element == null) {
                realtime.error('Current target is null in event: ' + JSON.stringify(evt));
            } else {
                var id = element.getAttribute('id');

                if (id != null) {
                    var content = {
                        id: id,
                        type: evt.type,
                        target: evt.target.getAttribute('id')
                    };
                    if ('keydown, keypress, keyup'.indexOf(evt.type) != -1) {
                        content.altKey = evt.altKey;
                        content.char = evt.charCode;
                        content.ctrlKey = evt.ctrlKey;
                        content.key = evt.keyCode;
                        content.locale = evt.locale;
                        content.location = evt.location;
                        content.metaKey = evt.metaKey;
                        content.repeat = evt.repeat;
                        content.shiftKey = evt.shiftKey;
                    } else if (evt.type == 'change') {
                        content.value = this.changeValue(element);
                    }

                    var r = this;
                    var f = function () {
                        if (fireChange) {
                            r.fireChange(element);
                        }
                        if (maximumRate && maximumRate > 0) {
                            r.sendLimited(content.type + '.' + content.id, content, maximumRate);
                        } else {
                            r.send(content);
                        }
                    };

                    if (confirmMessage == null || confirm(confirmMessage)) {
                        if (delay != 0) {
                            setTimeout(f, delay);
                        } else {
                            f();
                        }
                    }
                    return !preventDefault;
                } else {
                    realtime.error('Element ID is null for realtime.event.' + evt.type);
                }
            }
        } catch(err) {
            realtime.error('Error occurred handling a JavaScript event: ' + err.message + ' for ' + JSON.stringify(evt), null, err);
        }
    },
    fireChange: function(element) {
        realtime.send({
            type: 'change',
            id: element.getAttribute('id'),
            value: realtime.changeValue(element)
        });
    },
    changeValue: function(element) {
        var tagName = element.tagName.toLowerCase();
        var value = null;
        if (tagName == 'input') {
            var type = element.type.toLowerCase();
            if (type == 'checkbox' || type == 'radio') {
                value = element.checked;
            } else {
                value = element.value;
            }
        } else if (tagName == 'textarea') {
            value = element.value;
        } else if (tagName == 'select') {
            value = element.value;
        } else if (tagName == 'form') {
            var formData = $(element).serializeForm();
            value = '';
            jQuery.each(formData, function(k, v) {
                if (value != '') {
                    value += '&';
                }
                value += k;
                value += '=';
                value += v;
            });
        } else {
            realtime.error('Unsupported tag for change event: ' + tagName + ' (' + element.getAttribute('id') + ')');
        }
        return value;
    }
};

// Handle page reload support
realtime.on('reload', function(obj) {
    try {
        realtime.log('Reloading page...');
        document.location.reload(obj.forcedReload);
    } catch(err) {
        realtime.error('Failed to handle reload request', obj, err);
    }
});

// Handle InsertHTML
realtime.on('insertHTML', function(obj) {
    try {
        realtime.log('Insert HTML after: ' + obj.after + ', parent: ' + obj.parent + ', HTML: ' + obj.html + ', Append: ' + obj.append);
        if (obj.after != null) {
            var after = $('#' + obj.after);
            if (after.length == 0) {
                realtime.error('Unable to insert HTML after ' + obj.after + ' as the element is not in the hierarchy.');
            } else {
                after.after(obj.html);
            }
        } else {
            var parent = $('#' + obj.parent);
            if (parent.length == 0) {
                realtime.error('Unable to insert HTML under parent ' + obj.parent + ' as the element is not in the hierarchy.');
            } else if (obj.append) {
                parent.append(obj.html);
            } else {
                parent.prepend(obj.html);
            }
        }
    } catch(err) {
        realtime.error('Failed to handle insertHTML request', obj, err);
    }
});

// Handle InsertSVG
realtime.on('insertSVG', function(obj) {
    try {
        // TODO: re-write this to
        realtime.log('Insert SVG after: ' + obj.after + ', parent: ' + obj.parent + ', HTML: ' + obj.svg);
        if (obj.after != null) {
            var after = $('#' + obj.after);
            if (after.length == 0) {
                realtime.error('Unable to insert SVG after ' + obj.after + ' as the element is not in the hierarchy.');
            } else {
                after.after(parseSVG(obj.svg));
            }
        } else {
            var parent = $('#' + obj.parent);
            if (parent.length == 0) {
                realtime.error('Unable to insert SVG under parent ' + obj.parent + ' as the element is not in the hierarchy.');
            } else {
                parent.append(parseSVG(obj.svg));
            }
        }
    } catch(err) {
        realtime.error('Failed to handle insertSVG request', obj, err);
    }
});

// Handle RemoveHTML
realtime.on('removeHTML', function(obj) {
    try {
        realtime.log('Remove HTML! ' + obj.id);
        var element = document.getElementById(obj.id);
        if (element == null) {
            realtime.error('Unable to find element by id: ' + obj.id + ' remove.');
        } else {
            element.parentNode.removeChild(element);
        }
    } catch(err) {
        realtime.error('Failed to handle removeHTML request', obj, err);
    }
});

// Handle setting HTML attributes
realtime.on('attributeHTML', function(obj) {
    try {
        var element = document.getElementById(obj.id);
        if (element == null) {
            realtime.error('Unable to find element by id: ' + obj.id + ' to set attribute ' + obj.key + ' = ' + obj.value);
        } else {
            var tagName = element.tagName.toLowerCase();
            if (obj.value == null) {
                if (obj.key == 'checked') {
                    $(element).prop('checked', false);
                } else {
                    element.removeAttribute(obj.key);
                }
            } else if (obj.key == 'content') {
                if (tagName == 'textarea') {
                    $(element).val(obj.value);
                } else {
                    realtime.log('Setting inner html: ' + obj.value);
                    element.innerHTML = obj.value;
                }
            } else {
                realtime.log('setting attribute: ' + obj.key + ' = ' + obj.value + ' on ' + obj.id);
                if (obj.key == 'value') {
                    $(element).val(obj.value);
                } else {
                    element.setAttribute(obj.key, obj.value);
                    if (tagName == 'option' && obj.key == 'selected') {
                        element.parentNode.value = element.value;
                    }
                }
            }
        }
    } catch(err) {
        realtime.error('Failed to handle attributeHTML request', obj, err);
    }
});

// Handle selector stylization
realtime.on('setStyle', function(obj) {
    try {
        realtime.log('setting style!', obj);
        if (obj.styleSheet) {
            if (obj.key == null) {          // Clear style sheet
                $.stylesheet(obj.selector).css(null);
            } else {
                $.stylesheet(obj.selector, obj.key, obj.value);
            }
        } else {
            var selector = $(obj.selector);
            if (selector.length == 0) {
                realtime.error('Unable to find element by selector: ' + obj.selector + ' to set style ' + obj.key + ' = ' + obj.value);
            } else {
                if (obj.important) {
                    selector.style(obj.key, obj.value, 'important');
                } else {
                    selector.style(obj.key, obj.value);
                }
            }
        }
    } catch(err) {
        realtime.error('Failed to handle setStyle request', obj, err);
    }
});

// Handle evaluating arbitrary JavaScript from the server
realtime.on('eval', function(obj) {
    realtime.log('evaluating: ' + obj.code);
    var f = function() {
        if (obj.waitCondition == null || eval(obj.waitCondition)) {
            try {
                eval(obj.code);
            } catch(err) {
                realtime.error('Failed to handle eval request (' + err.message + ') - Code: "' + obj.code + '"', null, err);
            }
        } else {
            setTimeout(f, 10);
        }
    };
    f();
});

// Add support for the 'style' alternative to 'css' to allow setting importance
(function($) {
    if ($.fn.style) {
        return;
    }

    // Escape regex chars with \
    var escape = function(text) {
        return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
    };

    // For those who need them (< IE 9), add support for CSS functions
    var isStyleFuncSupported = !!CSSStyleDeclaration.prototype.getPropertyValue;
    if (!isStyleFuncSupported) {
        CSSStyleDeclaration.prototype.getPropertyValue = function(a) {
            return this.getAttribute(a);
        };
        CSSStyleDeclaration.prototype.setProperty = function(styleName, value, priority) {
            this.setAttribute(styleName, value);
            var priority = typeof priority != 'undefined' ? priority : '';
            if (priority != '') {
                // Add priority manually
                var rule = new RegExp(escape(styleName) + '\\s*:\\s*' + escape(value) +
                '(\\s*;)?', 'gmi');
                this.cssText =
                    this.cssText.replace(rule, styleName + ': ' + value + ' !' + priority + ';');
            }
        };
        CSSStyleDeclaration.prototype.removeProperty = function(a) {
            return this.removeAttribute(a);
        };
        CSSStyleDeclaration.prototype.getPropertyPriority = function(styleName) {
            var rule = new RegExp(escape(styleName) + '\\s*:\\s*[^\\s]*\\s*!important(\\s*;)?',
                'gmi');
            return rule.test(this.cssText) ? 'important' : '';
        }
    }

    // The style function
    $.fn.style = function(styleName, value, priority) {
        // DOM node
        var node = this.get(0);
        // Ensure we have a DOM node
        if (typeof node == 'undefined') {
            return this;
        }
        // CSSStyleDeclaration
        var style = this.get(0).style;
        // Getter/Setter
        if (typeof styleName != 'undefined') {
            if (typeof value != 'undefined') {
                // Set style property
                priority = typeof priority != 'undefined' ? priority : '';
                style.setProperty(styleName, value, priority);
                return this;
            } else {
                // Get style property
                return style.getPropertyValue(styleName);
            }
        } else {
            // Get CSSStyleDeclaration
            return style;
        }
    };
})(jQuery);

// TODO: remove this in favor of a completely new SVG implementation
function parseSVG(content) {
    var parser = new DOMParser();
    parser.async = false;
    content = content.toString().trim();
    content = '' + content + '';
    var document = parser.parseFromString(content, 'text/xml').documentElement;
    return document.firstChild;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy