diff --git a/public/sihnon-framework.js b/public/sihnon-framework.js new file mode 100644 index 0000000..ab8a205 --- /dev/null +++ b/public/sihnon-framework.js @@ -0,0 +1,537 @@ +/** + * Sihnon Framework JS Library + * + * Written by Ben Roberts + * Homepage: https://benroberts.net/projects/sihnon-framework/ + * Code: https://github.com/optiz0r/sihnon-js-lib/ + * + * Dependencies: + * - Bootstrap-*.js + * + * Released under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License + * http://creativecommons.org/licenses/by-nc-sa/3.0/ + */ + +/** + * Sihnon Framework object + * + * Entry point for all base framework code + */ +var sf = { + + /** + * Initialises the library + * + */ + init: function() { + + // Initialise all modules + modules = [ + 'utility', + 'ajax', + 'ui', + 'page', + 'actions', + ]; + + for (var module in modules) { + sf[module].init(); + } + + }, + + /** + * Utility module + * + * Contains simple and one-off functions that aren't worth grouping into full modules + */ + utility: { + + /** + * Initialise the module + */ + init: function() { + + }, + + /** + * Returns conditional if set to a value, else returns alternative + * + * @param conditional Value to test and return if set + * @param alternative Value to return if conditional is not set + * @returns + */ + orelse: function(conditional, alternative) { + if (typeof conditional != 'undefined') { + return conditional; + } else { + return alternative; + } + }, + + }, + + /** + * Code for making and handling AJAX requests + */ + ajax: { + + /** + * Initialise the module + */ + init: function() { + + }, + + /** + * Launch a simple AJAX GET request + * + * Makes an AJAX request to the specified URL and calls one of the given + * functions on completion. Default callbacks are executed if not provided. + * + * See also: sf.ajax.success, sf.ajax.failure + * + * @param url URL to request + * @param success Callback to execute on success + * @param failure Callback to execute on failure + */ + get: function(url, success, failure) { + $.ajax({ + url: url, + type: "GET", + dataType: "json", + success: sf.utility.orelse(success, sf.ajax.success), + error: sf.utility.orelse(failure, sf.ajax.failure), + }); + }, + + /** + * Launch a simple AJAX POST request + * + * Makes an AJAX request to the specified URL and calls one of the given + * functions on completion. Default callbacks are executed if not provided. + * + * See also: sf.ajax.success, sf.ajax.failure + * + * @param url URL to request + * @param data Data to send with the request + * @param success Callback to execute on success + * @param failure Callback to execute on failure + */ + post: function(url, data, success, failure) { + $.ajax({ + url: url, + type: "POST", + dataType: "json", + data: data, + success: sf.utility.orelse(success, sf.ajax.success), + error: sf.utility.orelse(failure, sf.ajax.failure), + }); + }, + + /** + * Default success handler + * + * Function that will be called for every successful AJAX request if no custom + * handler is defined. + * + * @param d Data returned from the request + * @param s Status of the request + * @param x XHttpRequest object used for the request + */ + success: function(d, s, x) { + + // Replace any requested parts of the page + sf.page.update(d.page_replacements); + + // Show any requested dialog + sf.ui.dialog.prepare(d.dialog); + + // Trigger any actions + sf.actions.triggerAll(d.actions); + }, + + failure: function(x, s, e) { + console.log("Ajax Failure: " + s, e); + console.log(x.responseText); + }, + + }, + + /** + * User Interface module + * + * Code for manipulating UI elements + */ + ui: { + + /** + * Initialise the module + */ + init: function() { + sf.ui.dialog.init(); + }, + + /** + * Submodule for customising and displaying the app dialog + */ + dialog: { + + /** + * Initialise the submodule + */ + init: function() { + // Permanently configure the X to close the dialog + $("#dialog-header-close").click(sf.dialog.close); + }, + + /** + * Prepare a dialog and optionally show it. + * + * Parameters should be an object which may contain any of the following fields: + * var parameters = { + * show: bool, // Show the dialog immediately after configuring + * title: string, // Title to display in the dialog header + * content: string, // Body of the dialog as HTML + * buttons: { // Configuration for the buttons in the dialog footer, see below + * type: ok|okcancel|yesno, // Type of buttons to display in the footer + * params: mixed, // Any data to be passed directly to the action handlers + * actions: { + * ok: string, // Action handler to be assigned to the OK button + * cancel: string, // Action handler to be assigned to the Cancel button + * yes: string, // Action handler to be assigned to the Yes button + * no: string, // Action handler to be assigned to the No button + * } + * } + * } + * + * var buttons = { + * type: ok|okcancel|yesno + * } + * + * + * @param p Parameters for configuring the dialog + */ + prepare: function(p) { + if ( ! p) { + return; + } + + if (p.buttons) { + switch (p.buttons.type) { + case 'ok': + $("#dialog-footer-ok-ok").click( + function() { + sf.actions.trigger(p.buttons.actions.ok, p.buttons.params); + } + ); + $("#dialog-footer-ok").show(); + break; + case 'okcancel': + $("#dialog-footer-okcancel-ok").click(function() { + sf.actions.trigger(p.buttons.actions.ok, p.buttons.params); + }); + $("#dialog-footer-okcancel-cancel").click(function() { + sf.actions.trigger(p.buttons.actions.cancel, p.buttons.params); + }); + $("#dialog-footer-okcancel").show(); + break; + case 'yesno': + $("#dialog-footer-yesno-yes").click( + function() { + sf.actions.trigger(p.buttons.actions.yes, p.buttons.params); + } + ); + $("#dialog-footer-yesno-no").click( + function() { + sf.actions.trigger(p.buttons.actions.no, p.buttons.params); + } + ); + $("#dialog-footer-yesno").show(); + break; + } + } + + if (p.title) { + $('#dialog-header-title').html(p.title); + } + + if (p.content) { + $('#dialog-body').html(p.content); + } + + if (p.show) { + sf.ui.dialog.show(); + } + }, + + /** + * Immediately show a pre-configured dialog + * + */ + show: function() { + $("#dialog").modal({ + show: true, + backdrop: true, + keyboard: true, + }); + }, + + /** + * Close the dialog and reset to default values + * + * The content and title will be removed, all buttons hidden + * and all action handlers removed. + * + */ + close: function() { + // Hide the dialog + $("#dialog").modal({ + show: false, + }); + + // Remove the dialog content + $("#dialog-body").html(''); + + // Hide all buttons + $(".dialog-footer-buttonset").hide(); + // Strip all event handlers + $(".dialog-footer-buttonset input[type='button']").unbind('click'); + }, + + /** + * Use the built-in dialog to display an error message + * + * Prepares the dialog with a pre-defined format using the details provided. + * The dialog is immediately shown with an OK button that cancels the dialog and + * takes no further action + * + * @param title Title for the error dialog + * @param content Content for the dialog + * @param messages An optional list of messages to be reported on the dialog, e.g. reasons for the failure described by content. + */ + error: function(title, content, messages) { + var formatted_content = $('
').text('content')); + if (messages) { + var formatted_messages = $('
').text('These messages were reported:').append(formatted_messages)); + } + + sf.ui.dialog.prepare({ + show: true, + title: title, + content: formatted_content, + buttons: { + type: 'ok', + actions: { + ok: 'close-dialog' + } + } + }); + }, + + }, + }, + + /** + * Page module + * + * Code for updating page content + */ + page: { + + /** + * List of callbacks to be executed on a new section of a page + * + * Used for setting up any behaviour on new page elements, such as when a page is + * first loaded, or replaced by an ajax call. + * + * Callbacks should have the form: + * function(d){} + * where d is the top level dom element being updated. + * + * New callbacks can be registered with addUpdateCallback() passing a unique name + * which can be used for later removing a callback if desired. + */ + update_callbacks: { + + }, + + /** + * Initialise module + */ + init: function() { + // Update the content of the page on first load + sf.page.updateEvents($('#page_content')); + }, + + /** + * Updates all given subsets of the page + * + * Takes an object defining the replacements to be made and updates the page + * + * Object should use a DOM ID as the key, and replacement content should sit under a content key, e.g.: + * + * replacements = { + * page_content: { + * content: 'This will replace the entire page!', + * }, + * } + * + * @param replacements Object containing replacements to make + */ + update: function(replacements) { + for ( var id in replacements) { + $("#" + id).html(replacements[id].content); + sf.page.updateEvents('#' + id); + } + }, + + /** + * Update a subset of the page content with any global events + * + * Called when the element d has been updated and callbacks need to be applied to + * the new content. + * + * @param elements ID or DOM element of the new content on which events need to be applied + */ + updateEvents: function(d) { + for (var c in sf.page.update_callbacks) { + sf.page.update_callbacks[c](d); + } + }, + + /** + * Add new page update callback + * + * Defines a new function to be run when part of the page is updated. + * This function will replace any previous callback registered with the same name. + * + * See also: sf.page.update_callbacks + * + * @param name Unique name of the callback for later retrieval + * @param callback Function to call + */ + addCallback: function(name, callback) { + sf.page.update_callbacks[name] = callback; + }, + + /** + * Remove a page update callback + * + * Removes a previously defined callback from the list of page update callbacks. + * + * See also: sf.page.update_callbacks + * + * @param name Unique name of the callback to be removed + */ + removeCallback: function(name) { + if (sf.page.update_callback[name]) { + delete sf.page.update_callbacks[name]; + } + }, + + }, + + /** + * Actions module + * + * Defines a list of named callbacks which can be executed from a non-trusted source + * such as an ajax response without requiring code evaluation. + */ + actions: { + + /** + * List of named callbacks that can be executed + * + * Callbacks should have the form: + * function(params){} + * where params is an action-specific list of parameters passed as an object. + * The callback should return a boolean to indicate success. + * + * New callbacks can be registered with addAction() passing a unique name + * which can be used for later removing an action if desired with removeAction(). + * + * Actions can be triggered manually using the trigger() method. A list of actions + * can be triggered in one go using triggerAll(). + */ + callbacks: { + + }, + + /** + * Initialise the module + */ + init: function() { + + }, + + /** + * Execute a named action + * + * Runs one or more named actions with the list of parameters provided + * All actions are run with the same parameters. + * + * @param names Names of the action(s) to execute + * @param params Object containing action specific parameters + * @returns bool + */ + trigger: function(names, params) { + if ( ! names instanceof Array) { + names = [names]; + } + + for (var name in names) { + if (sf.actions.callbacks[name]) { + return sf.actions.callbacks[name](params); + } + } + }, + + /** + * Executes all given actions + * + * @param actions Object containing names of all actions to execute with associated parameters + */ + triggerAll: function(actions) { + for (var action in actions) { + sf.actions.trigger(action, actions[action]); + } + }, + + /** + * Adds a new named action + * + * Defines a new named action to be run when manually triggered. + * This function will replace any previous action registered with the same name. + * + * See also: sf.actions.callbacks + * + * @param name Name of the new action + * @param callback Function to execute when this callback is triggered + */ + addAction: function(name, callback) { + sf.actions.callbacks[name] = callback; + }, + + /** + * Removes a previously registered named action + * + * @param name Name of the action to remove + */ + removeAction: function(name) { + if (sf.actions.callbacks[name]) { + delete sf.actions.callbacks[name]; + } + }, + }, + + +}; + +// Execute the initialisation code as soon as the DOM is ready. +$('document').ready(sb.init); \ No newline at end of file