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

SLING-INF.content.devwidgets.basiclti.javascript.basiclti.js Maven / Gradle / Ivy


/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

/*
 * Dependencies
 *
 * /dev/lib/misc/trimpath.template.js (TrimpathTemplates)
 */

require(['jquery', 'sakai/sakai.api.core'], function($, sakai) {

    /**
     * @name sakai_global.basiclti
     *
     * @class basiclti
     *
     * @description
     * Basiclti widget
     *
     * @version 0.0.1
     * @param {String} tuid Unique id of the widget
     * @param {Boolean} showSettings Show the settings of the widget or not
     */
    sakai_global.basiclti = function(tuid, showSettings){


        /////////////////////////////
        // Configuration variables //
        /////////////////////////////

        var $rootel = $('#' + tuid);
        var json = false;
        var isAdvancedSettingsVisible = false;

        // Default values
        var defaultWidth = 100;
        var defaultWidthUnit = '%';
        var defaultHeight = 200;

        // Links and labels
        var basiclti = '#basiclti';
        var basicltiSettings = basiclti + '_settings';
        var basicltiSettingsAdvanced = basicltiSettings + '_advanced';
        var basicltiSettingsAdvancedDown = basicltiSettingsAdvanced + '_down';
        var basicltiSettingsAdvancedToggleSettings = basicltiSettingsAdvanced + '_toggle_settings';
        var basicltiSettingsAdvancedUp = basicltiSettingsAdvanced + '_up';
        var basicltiSettingsBorders = basicltiSettings + '_borders';
        var basicltiSettingsCancel = basicltiSettings + '_cancel';
        var basicltiSettingsColorContainer = basicltiSettings + '_color_container';
        var basicltiSettingsHeight = basicltiSettings + '_frame_height';
        var basicltiSettingsInsert = basicltiSettings + '_insert';
        var basicltiSettingsPreview = basicltiSettings + '_preview';
        var basicltiSettingsPreviewId = tuid + '_frame';
        var basicltiSettingsPreviewFrame = '#' + basicltiSettingsPreviewId;
        var basicltiSettingsLtiUrl = basicltiSettings + '_ltiurl';
        var basicltiSettingsLtiKey = basicltiSettings + '_ltikey';
        var basicltiSettingsLtiSecret = basicltiSettings + '_ltisecret';
        var basicltiSettingsWidth = basicltiSettings + '_width';

        // Containers
        var basicltiMainContainer = basiclti + '_main_container';

        // Classes
        var basicltiSettingsWidthUnitClass = '.basiclti_settings_width_unit';
        var basicltiSettingsWidthUnitSelectedClass = 'basiclti_settings_width_unit_selected';

        // Templates
        var $basicltiSettingsColorContainerTemplate = $('#basiclti_settings_color_container_template', $rootel);
        var $basicltiSettingsTemplate = $('#basiclti_settings_template', $rootel);
        var $basicltiSettingsPreviewTemplate = $('#basiclti_settings_preview_template', $rootel);

        // see: http://www.ietf.org/rfc/rfc2396.txt Appendix B
        var urlRegExp = new RegExp('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?');


        ///////////////////////
        // Utility functions //
        ///////////////////////

        /**
         * Check if the value is a decimal or not
         * @param {Object} value Value that needs to be checked
         * @return {Boolean}
         *     true: is a decimal
         *     false: is not a decimal
         */
        var isDecimal = function(value){
            return (/^\d+$/).test(value);
        };

        /**
         * Check if the input url is in fact an url or not
         * @param {String} url Url that needs to be tested
         * @return {Boolean}
         *     true: is an url
         *     false: is not an url
         */
        var isUrl = function(url) {
            var matches = urlRegExp.exec(url);
            // e.g. if('http:' && 'localhost')
            if (matches[1] && matches[4]) {
                return true;
            } else {
                return false;
            }
        };

        /**
         * Check to see if both URLs are in the same origin. See: http://en.wikipedia.org/wiki/Same_origin_policy.
         * @param {String} url1
         * @param {String} url2
         * @return {Boolean}
         *     true: in the same origin policy
         *     false: NOT in the same origin policy
         */
        var isSameOriginPolicy = function(url1, url2) {
            if (url1 == url2) {
                return true;
            }
            // i.e. protocol, domain (and optional port numbers) must match
            if ((urlRegExp.exec(url1)[2] == urlRegExp.exec(url2)[2]) &&
               (urlRegExp.exec(url1)[4] == urlRegExp.exec(url2)[4])) {
                return true;
            } else {
                return false;
            }
        };

        /**
         * Called when the data has been saved to the JCR.
         */
        var savedDataToJCR = function() {
            displayRemoteContent(json);
            sakai.api.Widgets.Container.informFinish(tuid, 'basiclti');
        };


        //////////////////////
        // Render functions //
        //////////////////////

        /**
         * Render the iframe for the widget in settings mode
         * @param {Boolean} complete Render the preview completely or only adjust values
         */
        var renderIframeSettings = function(complete) {
            if (complete) { 
                json.launchDataUrl = sakai.config.URL.SDATA_FETCH_URL.replace(/__PLACEMENT__/, sakai.site.currentsite.id + '/_widgets').replace(/__TUID__/, tuid).replace(/__NAME__/, 'basiclti') + '.launch.html';               
                $(basicltiSettingsPreview, $rootel).html(sakai.api.Util.TemplateRenderer($basicltiSettingsPreviewTemplate, json));
            } else {
                $(basicltiSettingsPreviewFrame, $rootel).attr('style', 'border: ' + json.border_size + 'px #' + json.border_color + ' solid');
            }
        };

        /**
         * Render the iframe for the widget
         */
        var renderIframe = function() {
            if (json) {
                json.tuidFrame = basicltiSettingsPreviewId;
                $(basicltiMainContainer, $rootel).html(sakai.api.Util.TemplateRenderer($basicltiSettingsPreviewTemplate, json));
                json.launchDataUrl = sakai.api.Widgets.getWidgetDataStorageURL(tuid) + '.launch.html';
                $('#' + json.tuidFrame, $rootel).attr('src', json.launchDataUrl);

                // resize the iframe to match inner body height if in the same origin (i.e. same protocol/domain/port)
                if (isSameOriginPolicy(window.location.href, json.ltiurl)) {
                    $(basicltiSettingsPreviewFrame, $rootel).load(function() {
                        $(this).height($(this).contents().find('body').height() + 15); // add 10px for IE and 5px more for Gradebook weirdness
                    });
                }

                // SAKIII-314 We need to show the container, otherwise the second item won't be shown.
                $(basicltiMainContainer, $rootel).show();
            }
        };

        /**
         * Render the html of the basicltisettings
         */
        var renderRemoteContentSettings = function() {
            if (json) {
                $(basicltiSettings, $rootel).html(sakai.api.Util.TemplateRenderer($basicltiSettingsTemplate, json));
            }
        };

        /**
         * Render the color container
         */
        var renderColorContainer = function() {
            if (json) {
                $(basicltiSettingsColorContainer, $rootel).html(sakai.api.Util.TemplateRenderer($basicltiSettingsColorContainerTemplate, json));
            }
        };


        //////////////////////
        // Global functions //
        //////////////////////

        /**
         * Display the iframe in normal mode
         * @param {Object} parameters JSON object that contains the necessary information for the iframe
         */
        var displayRemoteContent = function(parameters) {
            // default to some reasonable vaules if the settings node does not have them (maybe a legacy node)
            if (parameters.border_size === null) {
                parameters.border_size = 0;
            }
            if (parameters.border_color === null) {
                parameters.border_color = 'cccccc';
            }
            if (parameters.width === null) {
                parameters.width = defaultWidth;
            }
            if (parameters.width_unit === null) {
                parameters.width_unit = defaultWidthUnit;
            }
            if (parameters.frame_height === null) {
                parameters.frame_height = defaultHeight;
            }
            json = parameters;
            renderIframe();
        };

        var saveContentAjax = function(savedata) {
            $.ajax({
                type: 'POST',
                url: sakai.api.Widgets.getWidgetDataStorageURL(tuid),
                data: savedata,
                success: function(data) {
                    savedDataToJCR(true, data);
                }
            });
        };

        /**
         * Save the basiclti to the jcr
         */
        var saveRemoteContent = function(){
 
            if (json.ltiurl !== '') {

                var savejson = {
                    ':operation': 'basiclti',
                    ':contentType': 'json',
                    ':replace': true,
                    ':replaceProperties': true,
                    '_charset_': 'utf-8'
                };

                var savejson_content = {
                    'sling:resourceType': 'sakai/basiclti',
                    'ltiurl': $(basicltiSettingsLtiUrl, $rootel).val() || '',
                    'ltikey': $(basicltiSettingsLtiKey, $rootel).val() || '',
                    'ltisecret': $(basicltiSettingsLtiSecret, $rootel).val() || '',
                    'debug@TypeHint': 'Boolean',
                    'debug': $('#basiclti_settings_debug:checked', $rootel).val() !== null,
                    'release_names@TypeHint': 'Boolean',
                    'release_names': $('#basiclti_settings_release_names:checked', $rootel).val() !== null,
                    'release_principal_name@TypeHint': 'Boolean',
                    'release_principal_name': $('#basiclti_settings_release_principal_name:checked', $rootel).val() !== null,
                    'release_email@TypeHint': 'Boolean',
                    'release_email': $('#basiclti_settings_release_email:checked', $rootel).val() !== null,
                    'launchDataUrl': '', // does not need to be persisted
                    'tuidFrame': '', // does not need to be persisted
                    'defined': '' // what the heck is this? Where does it come from?
                };
                savejson_content = $.extend({}, json, savejson_content);
                json = savejson_content;
                savejson[':content'] = $.toJSON(savejson_content);
                saveContentAjax(savejson);
            } else {
                sakai.api.Util.notification.show('', sakai.api.i18n.getValueForKey('PLEASE_SPECIFY_A_URL', 'basiclti'),
                                                 sakai.api.Util.notification.type.ERROR);
            }
        };

        /**
         * Change the direction (up/down) of the arrow for the advanced settings
         */
        var changeAdvancedSettingsArrow = function() {
            if (isAdvancedSettingsVisible) {
                $(basicltiSettingsAdvancedDown, $rootel).hide();
                $(basicltiSettingsAdvancedUp, $rootel).show();
            } else {
                $(basicltiSettingsAdvancedUp, $rootel).hide();
                $(basicltiSettingsAdvancedDown, $rootel).show();
            }
        };


        //////////////
        // Bindings //
        //////////////

        /*
         * Add binding to all the elements
         */
        var addBinding = function() {
            // Change the url for the iFrame
            $(basicltiSettingsLtiUrl, $rootel).on('change', function(){
                var urlValue = $(this).val();
                if (urlValue !== '') {
                    // Check if someone already wrote http inside the url
                    if (!isUrl(urlValue)) {
                        urlValue = 'http://' + urlValue;
                    }
                    json.ltiurl = urlValue;
                    //renderIframeSettings(true); // LDS disabled preview
                }
            });

            // Change the iframe width
            $(basicltiSettingsWidth, $rootel).on('change', function(){
                var widthValue = $(basicltiSettingsWidth, $rootel).val();

                if (isDecimal(widthValue)) {
                    json.width = widthValue;
                }
                renderIframeSettings(false);
            });

            // Change the iframe height
            $(basicltiSettingsHeight, $rootel).on('change', function(){
                var heightValue = $(basicltiSettingsHeight, $rootel).val();
                if (isDecimal(heightValue)) {
                    json.frame_height = heightValue;
                }
                renderIframeSettings(false);
            });

            // Change the border width
            $(basicltiSettingsBorders, $rootel).on('change', function() {
                var borderValue = $(basicltiSettingsBorders, $rootel).val();
                if (isDecimal(borderValue)) {
                    json.border_size = borderValue;
                    renderIframeSettings(false);
                }
            });

            // Toggle the advanced view
            $(basicltiSettingsAdvancedToggleSettings, $rootel).on('click', function() {
                $('#basiclti_settings_advanced', $rootel).toggle();
                isAdvancedSettingsVisible = !isAdvancedSettingsVisible;
                changeAdvancedSettingsArrow();
            });

            // When you click on one of the width units (px or percentage)
            $(basicltiSettingsWidthUnitClass, $rootel).on('click', function() {
                var widthUnitValue = $(this).attr('id').split('_')[$(this).attr('id').split('_').length - 1];
                if (widthUnitValue === 'px') {
                    json.width_unit = widthUnitValue;
                } else {
                    json.width_unit = '%';
                }
                $(basicltiSettingsWidthUnitClass, $rootel).removeClass(basicltiSettingsWidthUnitSelectedClass);
                $(this).addClass(basicltiSettingsWidthUnitSelectedClass);
                renderIframeSettings(false);
            });

            // Save the settings
            $(basicltiSettingsInsert, $rootel).on('click', function() {
                saveRemoteContent();
            });

            // Cancel the settings
            $(basicltiSettingsCancel, $rootel).on('click', function() {
                sakai.api.Widgets.Container.informCancel(tuid, 'basiclti');
            });

            $('.basiclti_settings_color', $rootel).on('click', function() {
                json.border_color = $(this).attr('id').split('_')[$(this).attr('id').split('_').length - 1];
                renderIframeSettings(false);
                renderColorContainer();
            });
        };


        ///////////////////////
        // Initial functions //
        ///////////////////////

        /**
         * Function that fills in the input fields in the settings tab.
         * @param {Object} parameters A JSON object that contains the necessary information.
         * @param {Boolean} exists Does there exist a previous basiclti
         */
        var displaySettings = function(parameters, exists) {
            if (exists && parameters.ltiurl) {
                if (parameters[':content']){
                    json = $.parseJSON(parameters[':content']);
                } else {
                    json = parameters;
                }
            } else { // use default values
                json = {
                    ltiurl: '',
                    ltikey: '',
                    ltisecret: '',
                    release_names: true,
                    release_principal_name: true,
                    release_email: true,
                    border_size: 0,
                    border_color: 'ccc',
                    frame_height: defaultHeight,
                    width: defaultWidth,
                    width_unit: defaultWidthUnit
                };
            }
            renderRemoteContentSettings();
            renderColorContainer();
            addBinding();
            changeAdvancedSettingsArrow();
            $(basicltiSettings, $rootel).show();
        };

        /**
         * Will fetch the URL and other parameters from the JCR and according to which
         * view we are in, fill in the settings or display an iframe.
         */
        var getRemoteContent = function() {
            sakai.api.Widgets.loadWidgetData(tuid, function(success,data){
                if (success) {
                    if (showSettings) {
                        displaySettings(data,true);
                    } else {
                        displayRemoteContent(data);
                    }
                } else {
                    displaySettings(null, false);
                }
            }, false);
        };

        /*
         * Is the widget in settings mode or not
         */
        if (showSettings) {
            $(basicltiMainContainer, $rootel).hide();
            $(basicltiSettings, $rootel).show();
        } else {
            $(basicltiSettings, $rootel).hide();
            $(basicltiMainContainer, $rootel).show();
        }

        getRemoteContent();
    };

    sakai.api.Widgets.widgetLoader.informOnLoad('basiclti');
});




© 2015 - 2025 Weber Informatics LLC | Privacy Policy