mirror of
https://github.com/humhub/humhub.git
synced 2025-03-14 20:19:47 +01:00
Added package.json, pjax dependencie + js rewrite enhancements
This commit is contained in:
parent
ceeaabedc7
commit
15cc36822c
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,6 +4,9 @@ assets/*
|
||||
protected/runtime/*
|
||||
!protected/runtime/.gitignore
|
||||
|
||||
node_modules/*
|
||||
!node_modules/.gitignore
|
||||
|
||||
protected/config/local/*
|
||||
!protected/config/local/.gitignore
|
||||
|
||||
|
18
Gruntfile.js
18
Gruntfile.js
@ -6,8 +6,8 @@ module.exports = function (grunt) {
|
||||
separator: ';',
|
||||
},
|
||||
dist: {
|
||||
src: ['js/humhub.init.js', 'js/humhub.util.js','js/humhub.scripts.js' ,'js/humhub.additions.js',
|
||||
'js/humhub.client.js', 'js/humhub.ui.js', 'js/humhub.modules.js', 'js/humhub.content.js',
|
||||
src: ['js/humhub.core.js', 'js/humhub.util.js' ,'js/humhub.additions.js',
|
||||
'js/humhub.client.js', 'js/humhub.ui.js', 'js/humhub.actions.js', 'js/humhub.content.js',
|
||||
'js/humhub.stream.js', 'js/humhub.ui.modal.js'],
|
||||
dest: 'js/dist/humhub.all.js'
|
||||
}
|
||||
@ -18,13 +18,21 @@ module.exports = function (grunt) {
|
||||
tasks: ['build']
|
||||
}
|
||||
},
|
||||
clean: ["assets/*"]
|
||||
clean: ["assets/*"],
|
||||
uglify: {
|
||||
build: {
|
||||
files: {
|
||||
'js/dist/humhub.all.min.js' : ['js/dist/humhub.all.js']
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
|
||||
grunt.registerTask('default', ['watch']);
|
||||
grunt.registerTask('build', ['concat', 'clean']);
|
||||
}
|
||||
grunt.registerTask('build', ['concat', 'uglify', 'clean']);
|
||||
};
|
@ -32,7 +32,8 @@
|
||||
"bower-asset/jquery-placeholder": "^2.3.0",
|
||||
"bower-asset/blueimp-file-upload": "9.11.*",
|
||||
"bower-asset/fontawesome": "^4.3.0",
|
||||
"bower-asset/bootstrap-markdown": "2.10.*"
|
||||
"bower-asset/bootstrap-markdown": "2.10.*",
|
||||
"bower-asset/jquery-pjax": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"yiisoft/yii2-codeception": "*",
|
||||
|
@ -173,7 +173,7 @@ function setModalHandler() {
|
||||
}
|
||||
|
||||
function initPlugins() {
|
||||
/*
|
||||
|
||||
// show Tooltips on elements inside the views, which have the class 'tt'
|
||||
$('.tt').tooltip({
|
||||
html: false,
|
||||
@ -189,7 +189,7 @@ function initPlugins() {
|
||||
// Replace the standard checkbox and radio buttons
|
||||
$('body').find(':checkbox, :radio').flatelements();
|
||||
|
||||
$('a[data-loader="modal"], button[data-loader="modal"]').loader();*/
|
||||
$('a[data-loader="modal"], button[data-loader="modal"]').loader();
|
||||
|
||||
}
|
||||
|
||||
|
1240
js/dist/humhub.all.js
vendored
1240
js/dist/humhub.all.js
vendored
File diff suppressed because it is too large
Load Diff
1
js/dist/humhub.all.min.js
vendored
Normal file
1
js/dist/humhub.all.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
162
js/humhub.actions.js
Normal file
162
js/humhub.actions.js
Normal file
@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Thid module can be used by humhub sub modules for registering handlers and serves as core module for executing actions triggered in the gui.
|
||||
* A module can either register global handler by using the registerHandler and registerAjaxHandler functions or use the content mechanism.
|
||||
*/
|
||||
humhub.initModule('actions', function (module, require, $) {
|
||||
var _handler = {};
|
||||
var object = require('util').object;
|
||||
var string = require('util').string;
|
||||
var client = require('client');
|
||||
|
||||
/**
|
||||
* Constructor for initializing the module.
|
||||
*/
|
||||
module.init = function () {
|
||||
//Binding default action types
|
||||
this.bindAction(document, 'click', '[data-action-click]');
|
||||
this.bindAction(document, 'dblclick', '[data-action-dblclick]');
|
||||
this.bindAction(document, 'change', '[data-action-mouseout]');
|
||||
|
||||
//Add addition for loader buttons
|
||||
require('additions').registerAddition('[data-action-load-button]', function () {
|
||||
var that = this;
|
||||
this.on('click.humhub-action-load-button', function (evt) {
|
||||
if (!that.find('.action-loader').length) {
|
||||
that.append('<span class="action-loader"><i class="fa fa-spinner fa-pulse"></i></span>');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers a given handler with the given id.
|
||||
*
|
||||
* This handler will be called e.g. after clicking a button with the handler id as
|
||||
* data-action-click attribute.
|
||||
*
|
||||
* The handler can access additional event information through the argument event.
|
||||
* The this object within the handler will be the trigger of the event.
|
||||
*
|
||||
* @param {string} id handler id should contain the module namespace
|
||||
* @param {function} handler function with one event argument
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.registerHandler = function (id, handler) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
_handler[id] = handler;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers an ajax eventhandler.
|
||||
* The function can either be called with four arguments (id, successhandler, errorhandler, additional config)
|
||||
* or with two (id, cfg) where tha handlers are contained in the config object itself.
|
||||
*
|
||||
* The successhandler will be called only if the response does not contain any errors or errormessages.
|
||||
* So the errorhandler is called for application and http errors.
|
||||
*
|
||||
* The config can contain additional ajax settings.
|
||||
*
|
||||
* @param {type} id
|
||||
* @param {type} success
|
||||
* @param {type} error
|
||||
* @param {type} cfg
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.registerAjaxHandler = function (id, success, error, cfg) {
|
||||
cfg = cfg || {};
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (object.isFunction(success)) {
|
||||
cfg.success = success;
|
||||
cfg.error = error;
|
||||
} else {
|
||||
cfg = success;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
_handler[id] = function (event) {
|
||||
var path = $(this).data('url-' + event.type) || $(this).data('url');
|
||||
client.ajax(path, cfg, event);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds an delegate wrapper event handler to the parent node. This is used to detect action handlers like
|
||||
* data-action-click events and map the call to either a stand alone handler or a content
|
||||
* action handler. The trigger of a contentAction has to be contained in a data-content-base node.
|
||||
*
|
||||
* This function uses the jQuery event delegation:
|
||||
*
|
||||
* $(parent).on(type, selector, function(){...});
|
||||
*
|
||||
* This assures the event binding for dynamic content (ajax content etc..)
|
||||
*
|
||||
* @see {@link humhub.modules.content.handleAction}
|
||||
* @param {Node|jQuery} parent - the event target
|
||||
* @param {string} type - event type e.g. click, change,...
|
||||
* @param {string} selector - jQuery selector
|
||||
* @param {string} selector - jQuery selector
|
||||
*/
|
||||
module.bindAction = function (parent, type, selector, directHandler) {
|
||||
parent = parent || document;
|
||||
var $parent = parent.jquery ? parent : $(parent);
|
||||
$parent.on(type+'.humhub-action', selector, function (evt) {
|
||||
evt.preventDefault();
|
||||
//The element which triggered the action e.g. a button or link
|
||||
$trigger = $(this);
|
||||
|
||||
//Get the handler id, either a stand alone handler or a content handler function e.g: 'edit'
|
||||
var handlerId = $trigger.data('action' + '-' + type);
|
||||
var event = {type: type, $trigger: $trigger, handler: handlerId};
|
||||
|
||||
event.finish = function() {
|
||||
_removeLoaderFromEventTarget(evt);
|
||||
};
|
||||
|
||||
//TODO: handle with $.Event
|
||||
//var event = $.Event(type, {$trigger: $trigger});
|
||||
//event.originalEvent = evt;
|
||||
|
||||
//Search and execute a stand alone handler or try to call the content action handler
|
||||
try {
|
||||
if(object.isFunction(directHandler)) {
|
||||
//Direct action handler
|
||||
directHandler.apply($trigger, [event]);
|
||||
} else if (_handler[handlerId]) {
|
||||
//Registered action handler
|
||||
var handler = _handler[handlerId];
|
||||
handler.apply($trigger, [event]);
|
||||
} else if (!_handler['humhub.modules.content.actiontHandler'](event)) { //Content action handler
|
||||
//If the content handler did not accept this event we try to find a handler by namespace
|
||||
var splittedNS = handlerId.split('.');
|
||||
var handler = splittedNS[splittedNS.length - 1];
|
||||
var target = require(string.cutsuffix(handlerId, '.' + handler));
|
||||
target[handler]({type: type, $trigger: $trigger});
|
||||
}
|
||||
} catch (e) {
|
||||
//TODO: handle error !
|
||||
console.error('Error while handling action event for handler "' + handlerId+'"', e);
|
||||
_removeLoaderFromEventTarget(evt);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _removeLoaderFromEventTarget = function (evt) {
|
||||
if (evt.target) {
|
||||
$target = $(evt.target);
|
||||
$loader = $target.find('.action-loader');
|
||||
|
||||
if ($loader.length) {
|
||||
$loader.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
@ -1,11 +1,22 @@
|
||||
/**
|
||||
* This can should be used as parent class for all content implementations
|
||||
* @type undefined|Function
|
||||
* This module manages UI-Additions registered by other modules. Additions can be applied to DOM elements
|
||||
* and are used to add a specific behaviour or manipulate the element itself. e.g: Richtext, Autosize input...
|
||||
*
|
||||
* An addition can be registered for a specific selector e.g: <input data-addition-richtext ... />
|
||||
* It is possible to register multiple additions for the same selector.
|
||||
*/
|
||||
humhub.additions = (function (module, $) {
|
||||
humhub.initModule('additions', function(module, require, $) {
|
||||
var _additions = {};
|
||||
|
||||
var registerAddition = function (selector, addition) {
|
||||
/**
|
||||
* Registers an addition for a given jQuery selector. There can be registered
|
||||
* multiple additions for the same selector.
|
||||
*
|
||||
* @param {string} selector jQuery selector
|
||||
* @param {function} addition addition function
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.registerAddition = function (selector, addition) {
|
||||
if(!_additions[selector]) {
|
||||
_additions[selector] = [];
|
||||
}
|
||||
@ -13,20 +24,28 @@ humhub.additions = (function (module, $) {
|
||||
_additions[selector].push(addition);
|
||||
};
|
||||
|
||||
var applyTo = function(element) {
|
||||
/**
|
||||
* Applies all matched additions to the given element and its children
|
||||
* @param {type} element
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.applyTo = function(element) {
|
||||
var $element = $(element);
|
||||
$.each(_additions, function(selector, additions) {
|
||||
$.each(additions, function(i, addition) {
|
||||
$.each($element.find(selector).addBack(selector), function() {
|
||||
var $match = $(this);
|
||||
addition.apply($match, [$match, $element]);
|
||||
try {
|
||||
var $match = $(this);
|
||||
addition.apply($match, [$match, $element]);
|
||||
} catch(e) {
|
||||
console.error('Error while applying addition '+addition+' on selector '+selector);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
registerAddition: registerAddition,
|
||||
applyTo: applyTo
|
||||
};
|
||||
})(humhub.additions || {}, $);
|
||||
|
||||
module.init = function() {
|
||||
//TODO: apply to html on startup, the problem is this could crash legacy code.
|
||||
};
|
||||
});
|
@ -1,104 +1,203 @@
|
||||
humhub.client = (function (module, $) {
|
||||
/**
|
||||
* Manages the client/server communication. Handles humhub json api responses and
|
||||
* pjax requests.
|
||||
*/
|
||||
humhub.initModule('client', function (module, require, $) {
|
||||
var object = require('util').object;
|
||||
var scripts = require('scripts');
|
||||
|
||||
var init = function() {
|
||||
$.ajaxPrefilter('html', function(options, originalOptions, jqXHR) {
|
||||
debugger;
|
||||
console.log(options);
|
||||
var pjaxHandler = options.success;
|
||||
options.success = function(result, textStatus, xhr) {
|
||||
console.log(result);
|
||||
pjaxHandler(result, textStatus, xhr);
|
||||
};
|
||||
options.error = function(err) {
|
||||
debugger;
|
||||
};
|
||||
});
|
||||
|
||||
///TEESSS
|
||||
$.pjax.defaults.maxCacheLength = 0;
|
||||
$('a.dashboard').on('click', function(evt) {
|
||||
debugger;
|
||||
evt.preventDefault();
|
||||
$.pjax({url:$(this).attr('href'), container: '#main-content', maxCacheLength:0, timeout:2000});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Response Wrapper Object for
|
||||
* easily accessing common data
|
||||
* Response Wrapper Object for easily accessing common data
|
||||
*/
|
||||
var Response = function (data) {
|
||||
this.data = data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the response is a confirmation of the request
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Response.prototype.isConfirmation = function () {
|
||||
return this.data && (this.data.status === 0);
|
||||
return this.getStatus() === 0;
|
||||
};
|
||||
|
||||
//TODO: isValidationError status 2
|
||||
|
||||
/**
|
||||
* Checks if the response is marke
|
||||
* @returns {humhub.client_L5.Response.data.status|Boolean}
|
||||
*/
|
||||
Response.prototype.isError = function () {
|
||||
return this.data && this.data.status && (this.data.status > 0);
|
||||
return this.getStatus() > 0 || this.getErrors().length;
|
||||
};
|
||||
|
||||
Response.prototype.getStatus = function () {
|
||||
return (this.data && object.isDefined(this.data.status)) ? this.data.status : -1;
|
||||
};
|
||||
|
||||
Response.prototype.getContent = function () {
|
||||
return this.$wrapperContent.children();
|
||||
Response.prototype.getErrorTitle = function() {
|
||||
return (this.data) ? this.data.errorTitle : undefined;
|
||||
};
|
||||
|
||||
Response.prototype.getFirstError = function() {
|
||||
var errors = this.getErrors();
|
||||
if(errors.length) {
|
||||
return errors[0];
|
||||
}
|
||||
};
|
||||
|
||||
Response.prototype.setAjaxError = function(xhr, errorThrown, textStatus,data , status) {
|
||||
this.xhr = xhr;
|
||||
this.textStatus = textStatus;
|
||||
this.data = data || {};
|
||||
this.data.status = status || xhr.status;
|
||||
this.data.errors = [errorThrown];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of errors or an empty array so getErrors().length is always
|
||||
* safe.
|
||||
* @returns {array} error array or empty array
|
||||
*/
|
||||
Response.prototype.getErrors = function () {
|
||||
return this.data.errors;
|
||||
if (this.data) {
|
||||
var errors = this.data.errors || [];
|
||||
return (object.isString(errors)) ? [errors] : errors;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
Response.prototype.getErrorCode = function () {
|
||||
return this.data.errorCode;
|
||||
/**
|
||||
* Returns the raw content object. The content object can either be an
|
||||
* object with multiple partials {partialId: content string} or a single content string.
|
||||
* @param {type} id
|
||||
* @returns {undefined|humhub.client_L5.Response.data.content}1
|
||||
*/
|
||||
Response.prototype.getContent = function () {
|
||||
return this.data.content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the response partial. If no id is given we return the first partial
|
||||
* we find.
|
||||
* @returns {humhub.client_L5.Response.data.content}
|
||||
*/
|
||||
Response.prototype.getPartial = function (id) {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
//TODO: handleResponse filter...
|
||||
if (object.isObject(this.data.content)) {
|
||||
return (id) ? this.data.content[id] : this.data.content;
|
||||
} else if (!id) {
|
||||
return this.data.content;
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
Response.prototype.toString = function () {
|
||||
return "{ status: " + this.data.status + " error: " + this.data.error + " data: " + this.data.data + " }";
|
||||
return "{ status: " + this.getStatus() + " error: " + this.getErrors() + " data: " + this.getContent() + " }";
|
||||
};
|
||||
|
||||
var errorHandler = function (cfg, xhr, type, errorThrown, errorCode, path) {
|
||||
errorCode = (xhr) ? xhr.status : parseInt(errorCode);
|
||||
console.warn("AjaxError: " + type + " " + errorThrown + " - " + errorCode);
|
||||
|
||||
if (cfg.error && object.isFunction(cfg.error)) {
|
||||
// "timeout", "error", "abort", "parsererror" or "application"
|
||||
//TODO: den trigger als this verwenden
|
||||
cfg.error(errorThrown, errorCode, type);
|
||||
} else {
|
||||
console.warn('Unhandled ajax error: ' + path + " type" + type + " error: " + errorThrown);
|
||||
}
|
||||
var submit = function ($form, cfg) {
|
||||
var cfg = cfg || {};
|
||||
$form = object.isString($form) ? $($form) : $form;
|
||||
cfg.type = $form.attr('method') || 'post';
|
||||
cfg.data = $form.serialize()
|
||||
ajax($form.attr('action'), cfg);
|
||||
};
|
||||
|
||||
var ajax = function (path, cfg) {
|
||||
var cfg = cfg || {};
|
||||
var async = cfg.async || true;
|
||||
var dataType = cfg.dataType || "json";
|
||||
|
||||
var error = function (xhr, type, errorThrown, errorCode) {
|
||||
errorHandler(cfg, xhr, type, errorThrown, errorCode, path);
|
||||
|
||||
var error = function (xhr, textStatus, errorThrown, data, status) {
|
||||
//Textstatus = "timeout", "error", "abort", "parsererror", "application"
|
||||
if (cfg.error && object.isFunction(cfg.error)) {
|
||||
var response = new Response();
|
||||
response.setAjaxError(xhr, errorThrown, textStatus, data, status);
|
||||
cfg.error(response);
|
||||
} else {
|
||||
console.warn('Unhandled ajax error: ' + path + " type" + type + " error: " + errorThrown);
|
||||
}
|
||||
};
|
||||
|
||||
var success = function (json) {
|
||||
handleResponse(json, function(response) {
|
||||
if (response.isError()) { //Application errors
|
||||
return error(undefined, "application", response.getError(), response.getErrorCode());
|
||||
} else if (cfg.success) {
|
||||
cfg.success(response);
|
||||
}
|
||||
});
|
||||
var success = function (json, textStatus, xhr) {
|
||||
var response = new Response(json);
|
||||
if (response.isError()) { //Application errors
|
||||
return error(xhr, "application", response.getErrors(), json, response.getStatus() );
|
||||
} else if (cfg.success) {
|
||||
response.textStatus = textStatus;
|
||||
response.xhr = xhr;
|
||||
cfg.success(response);
|
||||
}
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: path,
|
||||
data : cfg.data,
|
||||
data: cfg.data,
|
||||
type: cfg.type,
|
||||
beforeSend:cfg.beforeSend,
|
||||
beforeSend: cfg.beforeSend,
|
||||
processData: cfg.processData,
|
||||
contentType: cfg.contentType,
|
||||
async: async,
|
||||
dataType: dataType,
|
||||
success: success,
|
||||
error: error
|
||||
error: error
|
||||
});
|
||||
};
|
||||
|
||||
var handleResponse = function(json, callback) {
|
||||
var response = new Response(json);
|
||||
if(json.content) {
|
||||
response.$wrapperContent = $('<div id="respone-wrapper-container">'+json.content+'</div>');
|
||||
|
||||
//Find all remote scripts and remove them from the partial
|
||||
var scriptSrcArr = [];
|
||||
response.$wrapperContent.find('script[src]').each(function() {
|
||||
scriptSrcArr.push($(this).attr('src'));
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
//Load the remote scripts synchronously only if they are not already loaded.
|
||||
humhub.scripts.loadOnce(scriptSrcArr, true, function() {
|
||||
|
||||
module.export({
|
||||
ajax: ajax,
|
||||
submit: submit,
|
||||
init: init
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
var handleResponse = function (json, callback) {
|
||||
var response = new Response(json);
|
||||
if (json.content) {
|
||||
response.$content = $('<div>' + json.content + '</div>');
|
||||
|
||||
//Find all remote scripts and remove them from the partial
|
||||
var scriptSrcArr = [];
|
||||
response.$content.find('script[src]').each(function () {
|
||||
scriptSrcArr.push($(this).attr('src'));
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
//Load the remote scripts synchronously only if they are not already loaded.
|
||||
scripts.loadOnceSync(scriptSrcArr, function () {
|
||||
callback(response);
|
||||
});
|
||||
} else {
|
||||
callback(response);
|
||||
});
|
||||
} else {
|
||||
callback(response);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
ajax : ajax
|
||||
};
|
||||
})(humhub.client || {}, $);
|
||||
}
|
||||
};
|
||||
*/
|
@ -1,10 +1,56 @@
|
||||
/**
|
||||
* This can should be used as parent class for all content implementations
|
||||
* This module provides an api for handling content objects e.g. Posts, Polls...
|
||||
*
|
||||
* @type undefined|Function
|
||||
*/
|
||||
humhub.modules.content = (function (module, $) {
|
||||
|
||||
humhub.initModule('content', function(module, require, $) {
|
||||
var client = require('client');
|
||||
var object = require('util').object;
|
||||
var actions = require('actions');
|
||||
|
||||
var Content = module.Content = function(id) {
|
||||
module.init = function() {
|
||||
actions.registerHandler('humhub.modules.content.actiontHandler', function(event) {
|
||||
return module.handleAction(event);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given contentAction event. The event should provide the following properties:
|
||||
*
|
||||
* $trigger (required) : the trigger node of the event
|
||||
* handler (required) : the handler functionn name to be executed on the content
|
||||
* type (optoinal) : the event type 'click', 'change',...
|
||||
*
|
||||
* @param {object} event - event object
|
||||
* @returns {Boolean} true if the contentAction could be executed else false
|
||||
*/
|
||||
module.handleAction = function(event) {
|
||||
var $contentBase = this.getContentBase(event.$trigger);
|
||||
if($contentBase.length) {
|
||||
//Initialize a content instance by means of the content-base type and execute the handler
|
||||
var ContentType = require($contentBase.data('content-base'));
|
||||
if(ContentType) {
|
||||
var content = new ContentType($contentBase);
|
||||
if(event.handler && content[event.handler]) {
|
||||
content[event.handler](event);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
console.error('No ContentType found for '+$contentBase.data('content-base'));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
module.getContentBase = function($element) {
|
||||
return $element.closest('[data-content-base]');
|
||||
};
|
||||
|
||||
var Content = function(id) {
|
||||
if(!id) { //Create content
|
||||
return;
|
||||
}
|
||||
if (typeof id === 'string') {
|
||||
this.id = id;
|
||||
this.$ = $('#' + id);
|
||||
@ -12,7 +58,6 @@ humhub.modules.content = (function (module, $) {
|
||||
this.$ = id;
|
||||
this.id = this.$.attr('id');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Content.prototype.getKey = function () {
|
||||
@ -20,42 +65,95 @@ humhub.modules.content = (function (module, $) {
|
||||
};
|
||||
|
||||
Content.prototype.getEditUrl = function () {
|
||||
return this.$.data('content-edit-url');
|
||||
var result = this.$.data('content-edit-url');
|
||||
if(!result) {
|
||||
var parentContent = this.getParentContentBase('[data-content-base]');
|
||||
if(parentContent) {
|
||||
return parentContent.getEditUrl();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
Content.prototype.edit = function () {
|
||||
var modal = humhub.ui.modal;
|
||||
Content.prototype.getParentContentBase = function() {
|
||||
var $parent = this.$.parent().closest('[data-content-base]');
|
||||
if($parent.length) {
|
||||
try {
|
||||
var ParentType = require($parent.data('content-base'));
|
||||
return new ParentType($parent);
|
||||
} catch(err) {
|
||||
console.error('Could not instantiate parent content base: '+$parent.data('content-base'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Content.prototype.create = function (addContentHandler) {
|
||||
//Note that this Content won't have an id, so the backend will create an instance
|
||||
this.edit(addContentHandler);
|
||||
};
|
||||
|
||||
Content.prototype.edit = function (successHandler) {
|
||||
var editUrl = this.getEditUrl();
|
||||
var contentId = this.getKey();
|
||||
var modal = require('ui.modal').global;
|
||||
|
||||
if(!editUrl || !contentId) {
|
||||
if(!editUrl) {
|
||||
//Todo: handle error
|
||||
console.error('No editUrl or contentId found for edit content action editUrl: '+editUrl+ ' contentId '+contentId);
|
||||
console.error('No editUrl found for edit content action editUrl: '+editUrl+ ' contentId '+contentId);
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
|
||||
humhub.client.ajax(editUrl, {
|
||||
client.ajax(editUrl, {
|
||||
data: {
|
||||
'id' : contentId
|
||||
},
|
||||
beforeSend: function() {
|
||||
modal.showLoader();
|
||||
$('#globalModal').show();
|
||||
modal.loader();
|
||||
},
|
||||
success: function(response) {
|
||||
modal.content(response.getContent());
|
||||
//Parse Javascript
|
||||
//Show Modal
|
||||
//Todo: render edit modal from result
|
||||
//Successfully retrieved the edit form, now show it within a modal
|
||||
modal.content(response.getContent(), function() {
|
||||
//Bind direct action handler we could use a global registeredHandler but this is more efficient
|
||||
actions.bindAction(modal.getBody(), 'click', '[data-content-save]', function(event) {
|
||||
client.submit(modal.getForm(), {
|
||||
success : function(response) {
|
||||
if(object.isFunction(successHandler)) {
|
||||
if(successHandler(response, modal)) {modal.close();};
|
||||
} else {
|
||||
that.replaceContent(response.getContent());
|
||||
//TODO: check for content.highlight
|
||||
modal.close();
|
||||
}
|
||||
event.finish();
|
||||
},
|
||||
error : function(error) {
|
||||
//TODO: handle error
|
||||
modal.error(error);
|
||||
console.error('Error while submitting form :'+error);
|
||||
event.finish();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
error: function(err) {
|
||||
console.log(err);
|
||||
error: function(errResponse) {
|
||||
modal.error(errResponse);
|
||||
console.log('Error occured while editing content: '+errResponse.getFirstError());
|
||||
//Todo: handle error
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Content.prototype.replaceContent = function(content) {
|
||||
try {
|
||||
this.$.html($(content).children());
|
||||
} catch(e) {
|
||||
console.error('Error occured while replacing content: '+this.id , e);
|
||||
}
|
||||
};
|
||||
|
||||
Content.prototype.delete = function () {
|
||||
//Search for data-content-delte-url on root.
|
||||
//if(this.deleteModal) {open modal bla}
|
||||
@ -63,5 +161,5 @@ humhub.modules.content = (function (module, $) {
|
||||
//Trigger delete event
|
||||
};
|
||||
|
||||
return module;
|
||||
})(humhub.modules || {}, $);
|
||||
module.Content = Content;
|
||||
});
|
203
js/humhub.core.js
Normal file
203
js/humhub.core.js
Normal file
@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Sets up the humhub namespace and module management.
|
||||
* This namespace provides the following functions:
|
||||
*
|
||||
* initModule - for adding modules to this namespace and initializing them
|
||||
*
|
||||
* @type @exp;humhub|@call;humhub.core_L4|Function
|
||||
*/
|
||||
var humhub = humhub || (function($) {
|
||||
/**
|
||||
* Contains all modules by namespace e.g. modules.ui.modal
|
||||
* @type object
|
||||
*/
|
||||
var modules = {};
|
||||
|
||||
/**
|
||||
* Used to collect modules added while initial page load.
|
||||
* These modules will be intitialized after the document is ready.
|
||||
* @type Array
|
||||
*/
|
||||
var initialModules = [];
|
||||
|
||||
/**
|
||||
* Is set wehen document is ready
|
||||
* @type Boolean
|
||||
*/
|
||||
var initialized = false;
|
||||
|
||||
/**
|
||||
* Adds an module to the namespace. And initializes either after dom is ready.
|
||||
* The id can be provided either as
|
||||
*
|
||||
* - full namespace humhub.modules.ui.modal
|
||||
* - or module.ui.modal
|
||||
* - or short ui.modal
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* humhub.initModule('ui.modal', function(module, require, $) {...});
|
||||
*
|
||||
* This would create an empty ui namespace (if not already created before) and
|
||||
* initializes the given module.
|
||||
*
|
||||
* The module can export functions ans properties by
|
||||
* using either
|
||||
*
|
||||
* module.myFunction = function() {...}
|
||||
* ...
|
||||
*
|
||||
* or
|
||||
*
|
||||
* module.export({
|
||||
* myFunction: function() {...},
|
||||
* ...
|
||||
* });
|
||||
*
|
||||
* The export function can be called as often as needed (but should be called
|
||||
* once at the end of the module).
|
||||
* A module can provide an init function, which is called automatically
|
||||
* after the document is ready.
|
||||
*
|
||||
* Dependencies:
|
||||
*
|
||||
* The core modules are initialized in a specific order to provide the needed
|
||||
* dependencies for each module. The order is given by the order of initModule calls
|
||||
* and in case of core modules configured in the build script.
|
||||
*
|
||||
* A module can be received by using the required function within a module bock.
|
||||
* You can either depend on a module at initialisation time or within your functions.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* var modal = require('ui.modal);
|
||||
*
|
||||
* @param {type} id the namespaced id
|
||||
* @param {type} module
|
||||
* @returns {undefined}
|
||||
*/
|
||||
var initModule = function(id, module) {
|
||||
//Create module in the namespace and add helper functions
|
||||
var instance = resolveNameSpace(id, true);
|
||||
instance.id = 'humhub.modules.'+_cutModulePrefix(id);
|
||||
instance.require = require;
|
||||
instance.export = function(exports) {
|
||||
$.extend(instance, exports);
|
||||
};
|
||||
|
||||
//Setup the module
|
||||
module(instance, require, $);
|
||||
|
||||
//Initialize the module when document is ready
|
||||
if(!initialized) {
|
||||
initialModules.push(instance);
|
||||
} else {
|
||||
instance.init();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a module by its namespace e.g:
|
||||
*
|
||||
* For the module humhub.modules.ui.modal you can search:
|
||||
*
|
||||
* require('ui.modal');
|
||||
* require('modules.ui.modal');
|
||||
* require('humhub.modules.ui.modal');
|
||||
*
|
||||
* @param {type} moduleId
|
||||
* @returns object - the module instance if already initialized else undefined
|
||||
*
|
||||
* */
|
||||
var require = function(moduleNS) {
|
||||
var module = resolveNameSpace(moduleNS);
|
||||
if(!module) {
|
||||
//TODO: load remote module dependencies
|
||||
console.warn('No module found for id: '+moduleNS);
|
||||
}
|
||||
return module;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search the given module namespace, and creates the given namespace
|
||||
* if init = true.
|
||||
* @param {type} typePath the searched module namespace
|
||||
* @param {Boolean} init - if set to true, creates namespaces if not already present
|
||||
* @returns object - the given module
|
||||
*/
|
||||
var resolveNameSpace = function(typePath, init) {
|
||||
try {
|
||||
//cut humhub.modules prefix if present
|
||||
var moduleSuffix = _cutModulePrefix(typePath);
|
||||
|
||||
//Iterate through the namespace and return the last entry
|
||||
var result = modules;
|
||||
$.each(moduleSuffix.split('.'), function(i, subPath) {
|
||||
if(subPath in result) {
|
||||
result = result[subPath];
|
||||
} else if(init) {
|
||||
result = result[subPath] = {};
|
||||
} else {
|
||||
result = undefined; //path not found
|
||||
return false; //leave each loop
|
||||
}
|
||||
});
|
||||
return result;
|
||||
} catch(e) {
|
||||
//TODO: handle could not resolve type/namespace error
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cuts the prefix humub.modules or modules. from the given value.
|
||||
* @param {type} value
|
||||
* @returns {unresolved}
|
||||
*/
|
||||
var _cutModulePrefix = function(value) {
|
||||
return _cutPrefix(_cutPrefix(value, 'humhub.'), 'modules.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Cuts a prefix from a string, this is already available in humhub.util but
|
||||
* this is not accessible here.
|
||||
*
|
||||
* @param {type} value
|
||||
* @param {type} prefix
|
||||
* @returns {unresolved}
|
||||
*/
|
||||
var _cutPrefix = function(value, prefix) {
|
||||
if(!_startsWith(value, prefix)) {
|
||||
return value;
|
||||
}
|
||||
return value.substring(prefix.length, value.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a string strats with a given prefix
|
||||
* @param {type} val
|
||||
* @param {type} prefix
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
var _startsWith = function(val, prefix) {
|
||||
if(!val || !prefix) {
|
||||
return false;
|
||||
}
|
||||
return val.indexOf(prefix) === 0;
|
||||
};
|
||||
|
||||
//Initialize all initial modules
|
||||
$(document).ready(function() {
|
||||
$.each(initialModules, function(i, module) {
|
||||
if(module.init) {
|
||||
module.init();
|
||||
}
|
||||
initialized = true;
|
||||
console.log('Module initialized: '+module.id);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
initModule: initModule
|
||||
};
|
||||
})($);
|
@ -1 +0,0 @@
|
||||
var humhub = humhub || {};
|
@ -1,162 +0,0 @@
|
||||
/**
|
||||
* Thid module can be used by humhub sub modules for registering handlers and serves as core module for executing actions triggered in the gui.
|
||||
* A module can either register global handler by using the registerHandler and registerAjaxHandler functions or use the content mechanism.
|
||||
*/
|
||||
humhub.modules = (function (module, $) {
|
||||
var _handler = {};
|
||||
var object = humhub.util.object;
|
||||
var stringUtil = humhub.util.string;
|
||||
|
||||
/**
|
||||
* Constructor for initializing the module.
|
||||
*/
|
||||
module = function() {
|
||||
$eventDelegate = $('[data-event-delegate]').first() || document;
|
||||
|
||||
//Binding default action types
|
||||
this.bindAction(document, 'click', '[data-action-click]');
|
||||
this.bindAction(document, 'dblclick', '[data-action-dblclick]');
|
||||
this.bindAction(document, 'change', '[data-action-mouseout]');
|
||||
|
||||
/**
|
||||
* Handler for handling content actions
|
||||
*/
|
||||
this.registerHandler('humhub.modules.contentHandler', function(event) {
|
||||
if(!event.$contentBase) {
|
||||
console.error('Tried to register contentHandler with no contentBase handler: "'+ event.handler +
|
||||
'" type: "'+ event.type +
|
||||
'" $trigger: "'+ $trigger +
|
||||
'" $contentBase: "'+ $contentBase +
|
||||
'" handler: "'+ handlerId +'"');
|
||||
return;
|
||||
}
|
||||
|
||||
//Initialize a content instance by means of the content-base type and execute the handler
|
||||
var ContentType = humhub.modules.resolveType(event.$contentBase.data('content-base'));
|
||||
if(ContentType) {
|
||||
var content = new ContentType(event.$contentBase);
|
||||
if(event.handler && content[event.handler]) {
|
||||
content[event.handler](event);
|
||||
}
|
||||
} else {
|
||||
console.error('No ContentType found for '+event.$contentBase.data('content-base')+ ' event:', event);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers a given handler with the given id.
|
||||
*
|
||||
* This handler will be called e.g. after clicking a button with the handler id as
|
||||
* data-action-click attribute.
|
||||
*
|
||||
* The handler can access additional event information through the argument event.
|
||||
* The this object within the handler will be the trigger of the event.
|
||||
*
|
||||
* @param {string} id handler id should contain the module namespace
|
||||
* @param {function} handler function with one event argument
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.prototype.registerHandler = function (id, handler) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
_handler[id] = handler;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers an ajax eventhandler.
|
||||
* The function can either be called with four arguments (id, successhandler, errorhandler, additional config)
|
||||
* or with two (id, cfg) where tha handlers are contained in the config object itself.
|
||||
*
|
||||
* The successhandler will be called only if the response does not contain any errors or errormessages.
|
||||
* So the errorhandler is called for application and http errors.
|
||||
*
|
||||
* The config can contain additional ajax settings.
|
||||
*
|
||||
* @param {type} id
|
||||
* @param {type} success
|
||||
* @param {type} error
|
||||
* @param {type} cfg
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.prototype.registerAjaxHandler = function (id, success, error, cfg) {
|
||||
cfg = cfg || {};
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (object.isFunction(success)) {
|
||||
cfg.success = success;
|
||||
cfg.error = error;
|
||||
} else {
|
||||
cfg = success;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
_handler[id] = function (event) {
|
||||
var path = $(this).data('url-' + event.type) || $(this).data('url');
|
||||
humhub.client.ajax(path, cfg, event);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds an wrapper event handler to parent. This is used to detect
|
||||
* action handlers like data-action-click events and maps the call to either
|
||||
* a contentBase
|
||||
* @param {type} parent
|
||||
* @param {type} type
|
||||
* @param {type} selector
|
||||
*/
|
||||
module.prototype.bindAction = function (parent, type, selector) {
|
||||
parent = parent || document;
|
||||
var $parent = parent.jquery ? parent : $(parent);
|
||||
$parent.on(type, selector, function (evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
//The element which triggered the action e.g. a button or link
|
||||
$trigger = $(this);
|
||||
//Get
|
||||
var handlerId = $trigger.data('action' + '-' + type);
|
||||
|
||||
if(_handler[handlerId]) {
|
||||
//Check if global handler was registered
|
||||
var handler = _handler[handlerId];
|
||||
var event = {type: type, $trigger: $trigger};
|
||||
handler.apply($trigger, [event]);
|
||||
} else {
|
||||
var $contentBase = $trigger.closest('[data-content-base]');
|
||||
//Check if event is possibly a content event
|
||||
if(!$contentBase) {
|
||||
console.error('No handler and no contentBase was found for '+handlerId);
|
||||
return;
|
||||
}
|
||||
|
||||
var handler = _handler['humhub.modules.contentHandler'];
|
||||
var event = {type: type, $trigger: $trigger, $contentBase: $contentBase, handler: handlerId};
|
||||
handler.apply($trigger, [event]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.prototype.resolveType = function(typePath) {
|
||||
try {
|
||||
moduleSuffix = stringUtil.cutprefix(typePath, 'humhub.modules.');
|
||||
var result = humhub.modules;
|
||||
$.each(moduleSuffix.split('.'), function(index, subPath) {
|
||||
result = result[subPath];
|
||||
});
|
||||
return result;
|
||||
} catch(e) {
|
||||
//TODO: handle could not resolve type/namespace error
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return new module();
|
||||
})(humhub.modules || {}, $);
|
@ -1,5 +1,14 @@
|
||||
humhub.scripts = (function (module, $) {
|
||||
|
||||
/**
|
||||
* This module is not in use since Yii provides an build in mechanism for blocking
|
||||
* ajax requests for scripts already loaded.
|
||||
*
|
||||
* This module provides functions to keep track of loaded scripts and load scripts
|
||||
* once synchronously or asynchronously.
|
||||
*
|
||||
* @param {type} param1
|
||||
* @param {type} param2
|
||||
*/
|
||||
humhub.initModule('scripts', function(module, require, $) {
|
||||
var _scripts = [];
|
||||
|
||||
$(document).ready(function() {
|
||||
@ -12,12 +21,8 @@ humhub.scripts = (function (module, $) {
|
||||
return url.split('?')[0];
|
||||
};
|
||||
|
||||
var loadOnce = function(urls, sync, callback) {
|
||||
var loadOnce = function(urls) {
|
||||
urls = $.isArray(urls) ? urls : [urls];
|
||||
|
||||
if(sync) {
|
||||
return _loadOnceSync(urls, callback);
|
||||
}
|
||||
|
||||
var promises = [];
|
||||
$.each(urls, function(index, scriptUrl) {
|
||||
@ -34,7 +39,7 @@ humhub.scripts = (function (module, $) {
|
||||
return $.when.apply(null, promises);
|
||||
};
|
||||
|
||||
var _loadOnceSync = function(urls, callback) {
|
||||
var loadOnceSync = function(urls, callback) {
|
||||
var deferred = new $.Deferred();
|
||||
var promise = deferred.promise();
|
||||
$.each(urls, function(index, scriptUrl) {
|
||||
@ -64,7 +69,6 @@ humhub.scripts = (function (module, $) {
|
||||
});
|
||||
|
||||
deferred.resolve();
|
||||
|
||||
};
|
||||
|
||||
var containsScript = function(url) {
|
||||
@ -73,7 +77,7 @@ humhub.scripts = (function (module, $) {
|
||||
$.each(_scripts, function(index, scriptUrl) {
|
||||
if(scriptUrl === url) {
|
||||
result = true;
|
||||
return false;
|
||||
return false; //leave each
|
||||
}
|
||||
});
|
||||
return result;
|
||||
@ -84,9 +88,10 @@ humhub.scripts = (function (module, $) {
|
||||
_scripts.push(url);
|
||||
};
|
||||
|
||||
return {
|
||||
module.export({
|
||||
loadOnce: loadOnce,
|
||||
loadOnceSync: loadOnceSync,
|
||||
containsScript: containsScript,
|
||||
addScript:addScript
|
||||
};
|
||||
})(humhub.scripts || {}, $);
|
||||
});
|
||||
});
|
@ -2,7 +2,7 @@
|
||||
* Core module for managing Streams and StreamItems
|
||||
* @type Function
|
||||
*/
|
||||
humhub.modules.stream = (function (module, $) {
|
||||
humhub.initModule('stream', function(module, require, $) {
|
||||
|
||||
var ENTRY_ID_SELECTOR_PREFIX = '#wallEntry_';
|
||||
var WALLSTREAM_ID = 'wallStream';
|
||||
@ -12,7 +12,7 @@ humhub.modules.stream = (function (module, $) {
|
||||
* @param {type} id
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.StreamItem = function (id) {
|
||||
var StreamItem = function (id) {
|
||||
if (typeof id === 'string') {
|
||||
this.id = id;
|
||||
this.$ = $('#' + id);
|
||||
@ -25,25 +25,25 @@ humhub.modules.stream = (function (module, $) {
|
||||
/**
|
||||
* Removes the stream item from stream
|
||||
*/
|
||||
module.StreamItem.prototype.remove = function () {
|
||||
StreamItem.prototype.remove = function () {
|
||||
this.$.remove();
|
||||
};
|
||||
|
||||
module.StreamItem.prototype.getContentKey = function () {}
|
||||
StreamItem.prototype.getContentKey = function () {}
|
||||
|
||||
module.StreamItem.prototype.edit = function () {
|
||||
StreamItem.prototype.edit = function () {
|
||||
//Search for data-content-edit-url on root.
|
||||
//Call this url with data-content-pk
|
||||
//Trigger delete event
|
||||
};
|
||||
|
||||
module.StreamItem.prototype.delete = function () {
|
||||
StreamItem.prototype.delete = function () {
|
||||
//Search for data-content-delte-url on root.
|
||||
//Call this url with data-content-pk
|
||||
//Trigger delete event
|
||||
};
|
||||
|
||||
module.StreamItem.prototype.getContent = function () {
|
||||
StreamItem.prototype.getContent = function () {
|
||||
return this.$.find('.content');
|
||||
};
|
||||
|
||||
@ -62,18 +62,18 @@ humhub.modules.stream = (function (module, $) {
|
||||
* @param {type} id
|
||||
* @returns {undefined}
|
||||
*/
|
||||
module.Stream = function (id) {
|
||||
var Stream = function (id) {
|
||||
this.id = id;
|
||||
this.$ = $('#' + id);
|
||||
};
|
||||
|
||||
module.Stream.prototype.getEntry = function (id) {
|
||||
Stream.prototype.getEntry = function (id) {
|
||||
//Search for data-content-base and try to initiate the Item class
|
||||
|
||||
return new module.Entry(this.$.find(ENTRY_ID_SELECTOR_PREFIX + id));
|
||||
};
|
||||
|
||||
module.Stream.prototype.wallStick = function (url) {
|
||||
Stream.prototype.wallStick = function (url) {
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
type: 'post',
|
||||
@ -93,7 +93,7 @@ humhub.modules.stream = (function (module, $) {
|
||||
});
|
||||
};
|
||||
|
||||
module.Stream.prototype.wallUnstick = function (url) {
|
||||
Stream.prototype.wallUnstick = function (url) {
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
type: 'post',
|
||||
@ -113,7 +113,7 @@ humhub.modules.stream = (function (module, $) {
|
||||
* @param {type} className
|
||||
* @param {type} id
|
||||
*/
|
||||
module.Stream.prototype.wallArchive = function (id) {
|
||||
Stream.prototype.wallArchive = function (id) {
|
||||
|
||||
url = wallArchiveLinkUrl.replace('-id-', id);
|
||||
|
||||
@ -147,7 +147,7 @@ humhub.modules.stream = (function (module, $) {
|
||||
* @param {type} className
|
||||
* @param {type} id
|
||||
*/
|
||||
module.Stream.prototype.wallUnarchive = function (id) {
|
||||
Stream.prototype.wallUnarchive = function (id) {
|
||||
url = wallUnarchiveLinkUrl.replace('-id-', id);
|
||||
|
||||
$.ajax({
|
||||
@ -165,18 +165,20 @@ humhub.modules.stream = (function (module, $) {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.getStream = function () {
|
||||
|
||||
var getStream = function () {
|
||||
if (!module.mainStream) {
|
||||
module.mainStream = new module.Stream(WALLSTREAM_ID);
|
||||
}
|
||||
return module.mainStream;
|
||||
};
|
||||
|
||||
module.getEntry = function (id) {
|
||||
var getEntry = function (id) {
|
||||
return module.getStream().getEntry(id);
|
||||
};
|
||||
|
||||
return module;
|
||||
})(humhub.modules || {}, $);
|
||||
|
||||
module.export({
|
||||
getStream : getStream,
|
||||
getEntry : getEntry
|
||||
});
|
||||
});
|
@ -1,8 +1,8 @@
|
||||
humhub.ui = (function (module, $) {
|
||||
//Init default additions
|
||||
humhub.additions.registerAddition('.autosize', function($match) {
|
||||
$match.autosize();
|
||||
});
|
||||
|
||||
return module;
|
||||
})(humhub.ui || {}, $);
|
||||
humhub.initModule('ui', function(module, require, $) {
|
||||
var additions = require('additions');
|
||||
module.init = function() {
|
||||
additions.registerAddition('.autosize', function($match) {
|
||||
$match.autosize();
|
||||
});
|
||||
}
|
||||
});
|
@ -1,52 +1,294 @@
|
||||
humhub.ui.modal = (function (module, $) {
|
||||
var Modal = function() {};
|
||||
|
||||
Modal.prototype.getModal = function() {
|
||||
if(!this.$global) {
|
||||
this.$global = $('#globalModal');
|
||||
this.initModal();
|
||||
/**
|
||||
* Module for creating an manipulating modal dialoges.
|
||||
* Normal layout of a dialog:
|
||||
*
|
||||
* <div class="modal">
|
||||
* <div class="modal-dialog">
|
||||
* <div class="modal-content">
|
||||
* <div class="modal-header"></div>
|
||||
* <div class="modal-body"></div>
|
||||
* <div class="modal-footer"></div>
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
*
|
||||
* @param {type} param1
|
||||
* @param {type} param2
|
||||
*/
|
||||
humhub.initModule('ui.modal', function (module, require, $) {
|
||||
var additions = require('additions');
|
||||
var actions = require('actions');
|
||||
|
||||
//Keeps track of all initialized modals
|
||||
var modals = [];
|
||||
|
||||
var TMPL_MODAL_CONTAINER = '<div class="modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display: none; background:rgba(0,0,0,0.1)"><div class="modal-dialog"><div class="modal-content"></div></div></div>';
|
||||
var TMPL_MODAL_HEADER = '<div class="modal-header"><button type="button" class="close" data-modal-close="true" aria-hidden="true">×</button><h4 class="modal-title"></h4></div>';
|
||||
var TMPL_MODAL_BODY = '<div class="modal-body"></div>';
|
||||
var ERROR_DEFAULT_TITLE = 'Error';
|
||||
var ERROR_DEFAULT_MESSAGE = 'An unknown error occured!';
|
||||
|
||||
/**
|
||||
* The Modal class can be used to create new modals or manipulate existing modals.
|
||||
* If the constructor finds an element with the given id we use the existing modal,
|
||||
* if the id is not already used, we create a new modal dom element.
|
||||
*
|
||||
* @param {string} id - id of the modal
|
||||
*/
|
||||
var Modal = function (id) {
|
||||
this.$modal = $('#' + id);
|
||||
if (!this.$modal.lengh) {
|
||||
this.createModal(id);
|
||||
}
|
||||
return this.$global;
|
||||
this.initModal();
|
||||
modals.push(this);
|
||||
};
|
||||
|
||||
Modal.prototype.initModal = function() {
|
||||
var that = this;
|
||||
|
||||
/**
|
||||
* Creates a new modal dom skeleton.
|
||||
* @param {type} id the modal id
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.createModal = function (id) {
|
||||
this.$modal = $(TMPL_MODAL_CONTAINER).attr('id', id);
|
||||
$('body').append(this.$modal);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes default modal events and sets initial data.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.initModal = function () {
|
||||
//Set the loader as default content
|
||||
this.reset();
|
||||
this.$global.on('click', '[data-modal-close]', function() {
|
||||
var that = this;
|
||||
|
||||
//Set default modal manipulation event handlers
|
||||
this.getDialog().on('click', '[data-modal-close]', function () {
|
||||
that.close();
|
||||
}).on('click', '[data-modal-clear-error]', function () {
|
||||
that.clearErrorMessage();
|
||||
}).on('click', function (evt) {
|
||||
//We don't want to bubble this event to the modal root
|
||||
evt.stopPropagation();
|
||||
});
|
||||
|
||||
//A click on modal root (background) will close the modal
|
||||
this.$modal.on('click', function () {
|
||||
that.close();
|
||||
});
|
||||
};
|
||||
|
||||
Modal.prototype.close = function() {
|
||||
this.$global.hide();
|
||||
this.$global.html('');
|
||||
this.reset();
|
||||
|
||||
/**
|
||||
* Closes the modal with fade animation and sets the loader content
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.close = function () {
|
||||
var that = this;
|
||||
this.$modal.fadeOut('fast', function () {
|
||||
that.getContent().html('');
|
||||
that.reset();
|
||||
});
|
||||
};
|
||||
|
||||
Modal.prototype.reset = function() {
|
||||
this.content('<div class="modal-dialog"><div class="modal-content"><div class="modal-body"><div class="loader"><div class="sk-spinner sk-spinner-three-bounce"><div class="sk-bounce1"></div><div class="sk-bounce2"></div><div class="sk-bounce3"></div></div></div></div></div></div>');
|
||||
|
||||
/**
|
||||
* Sets the loader content and shows the modal
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.loader = function () {
|
||||
this.reset();
|
||||
this.show();
|
||||
};
|
||||
|
||||
Modal.prototype.showLoader = function() {
|
||||
$(".modal-footer .btn").hide();
|
||||
$(".modal-footer .loader").removeClass("hidden");
|
||||
this.getModal().show();
|
||||
|
||||
/**
|
||||
* Sets the default content (a loader animation)
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.reset = function () {
|
||||
this.content('<div class="modal-body"><div class="loader"><div class="sk-spinner sk-spinner-three-bounce"><div class="sk-bounce1"></div><div class="sk-bounce2"></div><div class="sk-bounce3"></div></div></div></div>');
|
||||
this.isFilled = false;
|
||||
};
|
||||
|
||||
Modal.prototype.content = function(content) {
|
||||
|
||||
/**
|
||||
* Sets the given content and applies content additions.
|
||||
* @param {string|jQuery} content - content to be set
|
||||
* @param {function} callback - callback function is called after html was inserted
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.content = function (content, callback) {
|
||||
try {
|
||||
var that = this;
|
||||
console.log('add content modal');
|
||||
this.getModal().html(content).promise().always(function() {
|
||||
console.log('modal content added');
|
||||
humhub.additions.applyTo(that.getModal());
|
||||
this.clearErrorMessage();
|
||||
this.getContent().html(content).promise().always(function () {
|
||||
additions.applyTo(that.getContent());
|
||||
!callback || callback(this.$modal);
|
||||
});
|
||||
} catch(err) {
|
||||
this.isFilled = true;
|
||||
} catch (err) {
|
||||
console.error('Error while setting modal content', err);
|
||||
this.setErrorMessage(err.message);
|
||||
//We try to apply additions anyway
|
||||
humhub.additions.applyTo(that.getModal());
|
||||
additions.applyTo(that.$modal);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets an errormessage and title. This function either creates an standalone
|
||||
* error modal with title and message, or adds/replaces a errorboxmessage to
|
||||
* already exising and filled modals.
|
||||
* @param {type} title
|
||||
* @param {type} message
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.error = function (title, message) {
|
||||
|
||||
if (arguments.length === 1 && title) {
|
||||
message = (title.getFirstError) ? title.getFirstError() : title;
|
||||
title = (title.getErrorTitle) ? title.getErrorTitle() : ERROR_DEFAULT_TITLE;
|
||||
}
|
||||
|
||||
title = title || ERROR_DEFAULT_TITLE;
|
||||
message = message || ERROR_DEFAULT_MESSAGE;
|
||||
|
||||
//If there is no content yet we create an error only content
|
||||
if (!this.isFilled) {
|
||||
this.clear();
|
||||
this.setTitle(title);
|
||||
this.setBody('');
|
||||
this.setErrorMessage(message);
|
||||
this.$modal.show();
|
||||
} else {
|
||||
//TODO: allow to set errorMessage and title even for inline messages
|
||||
this.setErrorMessage(message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes existing error messages
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.clearErrorMessage = function () {
|
||||
var modalError = this.getErrorMessage();
|
||||
if (modalError.length) {
|
||||
modalError.fadeOut('fast', function () {
|
||||
modalError.remove();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds or replaces an errormessagebox
|
||||
* @param {type} message
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.setErrorMessage = function (message) {
|
||||
var $errorMessage = this.getErrorMessage();
|
||||
if ($errorMessage.length) {
|
||||
$errorMessage.css('opacity', 0);
|
||||
$errorMessage.text(message);
|
||||
$errorMessage.animate({'opacity': 1}, 'fast');
|
||||
} else {
|
||||
this.getBody().prepend('<div class="modal-error alert alert-danger">' + message + '</div>');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current errormessagebox
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@call;getContent@call;find}
|
||||
*/
|
||||
Modal.prototype.getErrorMessage = function () {
|
||||
return this.getContent().find('.modal-error');
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the modal
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.show = function () {
|
||||
this.$modal.show();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the modal content
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.clear = function () {
|
||||
this.getContent().empty();
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the modal content jQuery representation
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getContent = function () {
|
||||
//We use the :first selector since jQuery refused to execute javascript if we set content with inline js
|
||||
return this.$modal.find('.modal-content:first');
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the modal dialog jQuery representation
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getDialog = function () {
|
||||
return this.$modal.find('.modal-dialog');
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches for forms within the modal
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getForm = function () {
|
||||
return this.$modal.find('form');
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds or replaces a modal-title with close button and a title text.
|
||||
* @param {type} title
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.setTitle = function (title) {
|
||||
var $header = this.getHeader();
|
||||
if (!$header.length) {
|
||||
this.getContent().prepend($(TMPL_MODAL_HEADER));
|
||||
$header = this.getHeader();
|
||||
}
|
||||
$header.find('.modal-title').html(title);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds or replaces the current modal-body
|
||||
* @param {type} content
|
||||
* @returns {undefined}
|
||||
*/
|
||||
Modal.prototype.setBody = function (content) {
|
||||
var $body = this.getBody();
|
||||
if (!$body.length) {
|
||||
this.getContent().append($(TMPL_MODAL_BODY));
|
||||
$body = this.getBody();
|
||||
}
|
||||
$body.html(content);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the modal-header element
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getHeader = function () {
|
||||
return this.$modal.find('.modal-header');
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the modal-body element
|
||||
* @returns {humhub.ui.modal_L18.Modal.prototype@pro;$modal@call;find}
|
||||
*/
|
||||
Modal.prototype.getBody = function () {
|
||||
return this.$modal.find('.modal-body');
|
||||
};
|
||||
|
||||
return new Modal();
|
||||
})(humhub.ui.modal || {}, $);
|
||||
module.export({
|
||||
init: function () {
|
||||
module.global = new Modal('global-modal');
|
||||
},
|
||||
Modal: Modal
|
||||
});
|
||||
});
|
@ -1,16 +1,16 @@
|
||||
/**
|
||||
* Util module with sub module for object and string utility functions
|
||||
*/
|
||||
humhub.util = (function (module, $) {
|
||||
module.object = {
|
||||
humhub.initModule('util', function(module, require, $) {
|
||||
var object = {
|
||||
isFunction: function (obj) {
|
||||
return this.prototype.toString.call(obj) === '[object Function]';
|
||||
return $.isFunction(obj);
|
||||
},
|
||||
isObject: function (obj) {
|
||||
return $.isPlainObject(obj);
|
||||
},
|
||||
isJQuery: function (obj) {
|
||||
return obj.jquery;
|
||||
return this.isDefined(obj) && obj.jquery;
|
||||
},
|
||||
isArray: function(obj) {
|
||||
return $.isArray(obj);
|
||||
@ -19,7 +19,7 @@ humhub.util = (function (module, $) {
|
||||
return typeof obj === 'string';
|
||||
},
|
||||
isNumber: function (n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
return this.isDefined(n) && !isNaN(parseFloat(n)) && isFinite(n);
|
||||
},
|
||||
isBoolean: function (obj) {
|
||||
return typeof obj === 'boolean';
|
||||
@ -44,23 +44,32 @@ humhub.util = (function (module, $) {
|
||||
}
|
||||
};
|
||||
|
||||
module.string = {
|
||||
cutprefix : function(val, suffix) {
|
||||
return val.substring(suffix.length, val.length);
|
||||
var string = {
|
||||
cutprefix : function(val, prefix) {
|
||||
if(!this.startsWith(prefix)) {
|
||||
return val;
|
||||
}
|
||||
return val.substring(prefix.length, val.length);
|
||||
},
|
||||
cutsuffix : function(val, suffix) {
|
||||
return val.slice(0, suffix.length * -1);
|
||||
},
|
||||
startsWith : function(val, prefix) {
|
||||
if(!module.object.isDefined(val) || !module.object.isDefined(prefix)) {
|
||||
if(!object.isDefined(val) || !object.isDefined(prefix)) {
|
||||
return false;
|
||||
}
|
||||
return val.indexOf(prefix) === 0;
|
||||
},
|
||||
endsWith : function(val, suffix) {
|
||||
if(!module.object.isDefined(val) || !module.object.isDefined(suffix)) {
|
||||
if(!object.isDefined(val) || !object.isDefined(suffix)) {
|
||||
return false;
|
||||
}
|
||||
return val.indexOf(suffix, val.length - suffix.length) !== -1;
|
||||
}
|
||||
};
|
||||
|
||||
return module;
|
||||
})(humhub.util || {}, $);
|
||||
module.export({
|
||||
object: object,
|
||||
string: string
|
||||
});
|
||||
});
|
919
js/jquery.pjax.js
Normal file
919
js/jquery.pjax.js
Normal file
@ -0,0 +1,919 @@
|
||||
/*!
|
||||
* Copyright 2012, Chris Wanstrath
|
||||
* Released under the MIT License
|
||||
* https://github.com/defunkt/jquery-pjax
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
// When called on a container with a selector, fetches the href with
|
||||
// ajax into the container or with the data-pjax attribute on the link
|
||||
// itself.
|
||||
//
|
||||
// Tries to make sure the back button and ctrl+click work the way
|
||||
// you'd expect.
|
||||
//
|
||||
// Exported as $.fn.pjax
|
||||
//
|
||||
// Accepts a jQuery ajax options object that may include these
|
||||
// pjax specific options:
|
||||
//
|
||||
//
|
||||
// container - Where to stick the response body. Usually a String selector.
|
||||
// $(container).html(xhr.responseBody)
|
||||
// (default: current jquery context)
|
||||
// push - Whether to pushState the URL. Defaults to true (of course).
|
||||
// replace - Want to use replaceState instead? That's cool.
|
||||
//
|
||||
// For convenience the second parameter can be either the container or
|
||||
// the options object.
|
||||
//
|
||||
// Returns the jQuery object
|
||||
function fnPjax(selector, container, options) {
|
||||
var context = this
|
||||
return this.on('click.pjax', selector, function(event) {
|
||||
var opts = $.extend({}, optionsFor(container, options))
|
||||
if (!opts.container)
|
||||
opts.container = $(this).attr('data-pjax') || context
|
||||
handleClick(event, opts)
|
||||
})
|
||||
}
|
||||
|
||||
// Public: pjax on click handler
|
||||
//
|
||||
// Exported as $.pjax.click.
|
||||
//
|
||||
// event - "click" jQuery.Event
|
||||
// options - pjax options
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $(document).on('click', 'a', $.pjax.click)
|
||||
// // is the same as
|
||||
// $(document).pjax('a')
|
||||
//
|
||||
// $(document).on('click', 'a', function(event) {
|
||||
// var container = $(this).closest('[data-pjax-container]')
|
||||
// $.pjax.click(event, container)
|
||||
// })
|
||||
//
|
||||
// Returns nothing.
|
||||
function handleClick(event, container, options) {
|
||||
options = optionsFor(container, options)
|
||||
|
||||
var link = event.currentTarget
|
||||
|
||||
if (link.tagName.toUpperCase() !== 'A')
|
||||
throw "$.fn.pjax or $.pjax.click requires an anchor element"
|
||||
|
||||
// Middle click, cmd click, and ctrl click should open
|
||||
// links in a new tab as normal.
|
||||
if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
|
||||
return
|
||||
|
||||
// Ignore cross origin links
|
||||
if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
|
||||
return
|
||||
|
||||
// Ignore case when a hash is being tacked on the current URL
|
||||
if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
|
||||
return
|
||||
|
||||
// Ignore event with default prevented
|
||||
if (event.isDefaultPrevented())
|
||||
return
|
||||
|
||||
var defaults = {
|
||||
url: link.href,
|
||||
container: $(link).attr('data-pjax'),
|
||||
target: link
|
||||
}
|
||||
|
||||
var opts = $.extend({}, defaults, options)
|
||||
var clickEvent = $.Event('pjax:click')
|
||||
$(link).trigger(clickEvent, [opts])
|
||||
|
||||
if (!clickEvent.isDefaultPrevented()) {
|
||||
pjax(opts)
|
||||
event.preventDefault()
|
||||
$(link).trigger('pjax:clicked', [opts])
|
||||
}
|
||||
}
|
||||
|
||||
// Public: pjax on form submit handler
|
||||
//
|
||||
// Exported as $.pjax.submit
|
||||
//
|
||||
// event - "click" jQuery.Event
|
||||
// options - pjax options
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $(document).on('submit', 'form', function(event) {
|
||||
// var container = $(this).closest('[data-pjax-container]')
|
||||
// $.pjax.submit(event, container)
|
||||
// })
|
||||
//
|
||||
// Returns nothing.
|
||||
function handleSubmit(event, container, options) {
|
||||
options = optionsFor(container, options)
|
||||
|
||||
var form = event.currentTarget
|
||||
|
||||
if (form.tagName.toUpperCase() !== 'FORM')
|
||||
throw "$.pjax.submit requires a form element"
|
||||
|
||||
var defaults = {
|
||||
type: form.method.toUpperCase(),
|
||||
url: form.action,
|
||||
container: $(form).attr('data-pjax'),
|
||||
target: form
|
||||
}
|
||||
|
||||
if (defaults.type !== 'GET' && window.FormData !== undefined) {
|
||||
defaults.data = new FormData(form);
|
||||
defaults.processData = false;
|
||||
defaults.contentType = false;
|
||||
} else {
|
||||
// Can't handle file uploads, exit
|
||||
if ($(form).find(':file').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to manually serializing the fields
|
||||
defaults.data = $(form).serializeArray();
|
||||
}
|
||||
|
||||
pjax($.extend({}, defaults, options))
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
// Loads a URL with ajax, puts the response body inside a container,
|
||||
// then pushState()'s the loaded URL.
|
||||
//
|
||||
// Works just like $.ajax in that it accepts a jQuery ajax
|
||||
// settings object (with keys like url, type, data, etc).
|
||||
//
|
||||
// Accepts these extra keys:
|
||||
//
|
||||
// container - Where to stick the response body.
|
||||
// $(container).html(xhr.responseBody)
|
||||
// push - Whether to pushState the URL. Defaults to true (of course).
|
||||
// replace - Want to use replaceState instead? That's cool.
|
||||
//
|
||||
// Use it just like $.ajax:
|
||||
//
|
||||
// var xhr = $.pjax({ url: this.href, container: '#main' })
|
||||
// console.log( xhr.readyState )
|
||||
//
|
||||
// Returns whatever $.ajax returns.
|
||||
function pjax(options) {
|
||||
options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
|
||||
|
||||
if ($.isFunction(options.url)) {
|
||||
options.url = options.url()
|
||||
}
|
||||
|
||||
var target = options.target
|
||||
|
||||
var hash = parseURL(options.url).hash
|
||||
|
||||
var context = options.context = findContainerFor(options.container)
|
||||
|
||||
// We want the browser to maintain two separate internal caches: one
|
||||
// for pjax'd partial page loads and one for normal page loads.
|
||||
// Without adding this secret parameter, some browsers will often
|
||||
// confuse the two.
|
||||
if (!options.data) options.data = {}
|
||||
if ($.isArray(options.data)) {
|
||||
options.data.push({name: '_pjax', value: context.selector})
|
||||
} else {
|
||||
options.data._pjax = context.selector
|
||||
}
|
||||
|
||||
function fire(type, args, props) {
|
||||
if (!props) props = {}
|
||||
props.relatedTarget = target
|
||||
var event = $.Event(type, props)
|
||||
context.trigger(event, args)
|
||||
return !event.isDefaultPrevented()
|
||||
}
|
||||
|
||||
var timeoutTimer
|
||||
|
||||
options.beforeSend = function(xhr, settings) {
|
||||
// No timeout for non-GET requests
|
||||
// Its not safe to request the resource again with a fallback method.
|
||||
if (settings.type !== 'GET') {
|
||||
settings.timeout = 0
|
||||
}
|
||||
|
||||
xhr.setRequestHeader('X-PJAX', 'true')
|
||||
xhr.setRequestHeader('X-PJAX-Container', context.selector)
|
||||
|
||||
if (!fire('pjax:beforeSend', [xhr, settings]))
|
||||
return false
|
||||
|
||||
if (settings.timeout > 0) {
|
||||
timeoutTimer = setTimeout(function() {
|
||||
if (fire('pjax:timeout', [xhr, options]))
|
||||
xhr.abort('timeout')
|
||||
}, settings.timeout)
|
||||
|
||||
// Clear timeout setting so jquerys internal timeout isn't invoked
|
||||
settings.timeout = 0
|
||||
}
|
||||
|
||||
var url = parseURL(settings.url)
|
||||
if (hash) url.hash = hash
|
||||
options.requestUrl = stripInternalParams(url)
|
||||
}
|
||||
|
||||
options.complete = function(xhr, textStatus) {
|
||||
if (timeoutTimer)
|
||||
clearTimeout(timeoutTimer)
|
||||
|
||||
fire('pjax:complete', [xhr, textStatus, options])
|
||||
|
||||
fire('pjax:end', [xhr, options])
|
||||
}
|
||||
|
||||
options.error = function(xhr, textStatus, errorThrown) {
|
||||
var container = extractContainer("", xhr, options)
|
||||
|
||||
var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
|
||||
if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
|
||||
locationReplace(container.url)
|
||||
}
|
||||
}
|
||||
|
||||
options.success = function(data, status, xhr) {
|
||||
var previousState = pjax.state;
|
||||
|
||||
// If $.pjax.defaults.version is a function, invoke it first.
|
||||
// Otherwise it can be a static string.
|
||||
var currentVersion = (typeof $.pjax.defaults.version === 'function') ?
|
||||
$.pjax.defaults.version() :
|
||||
$.pjax.defaults.version
|
||||
|
||||
var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
|
||||
|
||||
var container = extractContainer(data, xhr, options)
|
||||
|
||||
var url = parseURL(container.url)
|
||||
if (hash) {
|
||||
url.hash = hash
|
||||
container.url = url.href
|
||||
}
|
||||
|
||||
// If there is a layout version mismatch, hard load the new url
|
||||
if (currentVersion && latestVersion && currentVersion !== latestVersion) {
|
||||
locationReplace(container.url)
|
||||
return
|
||||
}
|
||||
|
||||
// If the new response is missing a body, hard load the page
|
||||
if (!container.contents) {
|
||||
locationReplace(container.url)
|
||||
return
|
||||
}
|
||||
|
||||
pjax.state = {
|
||||
id: options.id || uniqueId(),
|
||||
url: container.url,
|
||||
title: container.title,
|
||||
container: context.selector,
|
||||
fragment: options.fragment,
|
||||
timeout: options.timeout
|
||||
}
|
||||
|
||||
if (options.push || options.replace) {
|
||||
window.history.replaceState(pjax.state, container.title, container.url)
|
||||
}
|
||||
|
||||
// Clear out any focused controls before inserting new page contents.
|
||||
try {
|
||||
document.activeElement.blur()
|
||||
} catch (e) { }
|
||||
|
||||
if (container.title) document.title = container.title
|
||||
|
||||
fire('pjax:beforeReplace', [container.contents, options], {
|
||||
state: pjax.state,
|
||||
previousState: previousState
|
||||
})
|
||||
context.html(container.contents)
|
||||
|
||||
// FF bug: Won't autofocus fields that are inserted via JS.
|
||||
// This behavior is incorrect. So if theres no current focus, autofocus
|
||||
// the last field.
|
||||
//
|
||||
// http://www.w3.org/html/wg/drafts/html/master/forms.html
|
||||
var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
|
||||
if (autofocusEl && document.activeElement !== autofocusEl) {
|
||||
autofocusEl.focus();
|
||||
}
|
||||
|
||||
executeScriptTags(container.scripts)
|
||||
|
||||
var scrollTo = options.scrollTo
|
||||
|
||||
// Ensure browser scrolls to the element referenced by the URL anchor
|
||||
if (hash) {
|
||||
var name = decodeURIComponent(hash.slice(1))
|
||||
var target = document.getElementById(name) || document.getElementsByName(name)[0]
|
||||
if (target) scrollTo = $(target).offset().top
|
||||
}
|
||||
|
||||
if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
|
||||
|
||||
fire('pjax:success', [data, status, xhr, options])
|
||||
}
|
||||
|
||||
|
||||
// Initialize pjax.state for the initial page load. Assume we're
|
||||
// using the container and options of the link we're loading for the
|
||||
// back button to the initial page. This ensures good back button
|
||||
// behavior.
|
||||
if (!pjax.state) {
|
||||
pjax.state = {
|
||||
id: uniqueId(),
|
||||
url: window.location.href,
|
||||
title: document.title,
|
||||
container: context.selector,
|
||||
fragment: options.fragment,
|
||||
timeout: options.timeout
|
||||
}
|
||||
window.history.replaceState(pjax.state, document.title)
|
||||
}
|
||||
|
||||
// Cancel the current request if we're already pjaxing
|
||||
abortXHR(pjax.xhr)
|
||||
|
||||
pjax.options = options
|
||||
var xhr = pjax.xhr = $.ajax(options)
|
||||
|
||||
if (xhr.readyState > 0) {
|
||||
if (options.push && !options.replace) {
|
||||
// Cache current container element before replacing it
|
||||
cachePush(pjax.state.id, cloneContents(context))
|
||||
|
||||
window.history.pushState(null, "", options.requestUrl)
|
||||
}
|
||||
|
||||
fire('pjax:start', [xhr, options])
|
||||
fire('pjax:send', [xhr, options])
|
||||
}
|
||||
|
||||
return pjax.xhr
|
||||
}
|
||||
|
||||
// Public: Reload current page with pjax.
|
||||
//
|
||||
// Returns whatever $.pjax returns.
|
||||
function pjaxReload(container, options) {
|
||||
var defaults = {
|
||||
url: window.location.href,
|
||||
push: false,
|
||||
replace: true,
|
||||
scrollTo: false
|
||||
}
|
||||
|
||||
return pjax($.extend(defaults, optionsFor(container, options)))
|
||||
}
|
||||
|
||||
// Internal: Hard replace current state with url.
|
||||
//
|
||||
// Work for around WebKit
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=93506
|
||||
//
|
||||
// Returns nothing.
|
||||
function locationReplace(url) {
|
||||
window.history.replaceState(null, "", pjax.state.url)
|
||||
window.location.replace(url)
|
||||
}
|
||||
|
||||
|
||||
var initialPop = true
|
||||
var initialURL = window.location.href
|
||||
var initialState = window.history.state
|
||||
|
||||
// Initialize $.pjax.state if possible
|
||||
// Happens when reloading a page and coming forward from a different
|
||||
// session history.
|
||||
if (initialState && initialState.container) {
|
||||
pjax.state = initialState
|
||||
}
|
||||
|
||||
// Non-webkit browsers don't fire an initial popstate event
|
||||
if ('state' in window.history) {
|
||||
initialPop = false
|
||||
}
|
||||
|
||||
// popstate handler takes care of the back and forward buttons
|
||||
//
|
||||
// You probably shouldn't use pjax on pages with other pushState
|
||||
// stuff yet.
|
||||
function onPjaxPopstate(event) {
|
||||
|
||||
// Hitting back or forward should override any pending PJAX request.
|
||||
if (!initialPop) {
|
||||
abortXHR(pjax.xhr)
|
||||
}
|
||||
|
||||
var previousState = pjax.state
|
||||
var state = event.state
|
||||
var direction
|
||||
|
||||
if (state && state.container) {
|
||||
// When coming forward from a separate history session, will get an
|
||||
// initial pop with a state we are already at. Skip reloading the current
|
||||
// page.
|
||||
if (initialPop && initialURL == state.url) return
|
||||
|
||||
if (previousState) {
|
||||
// If popping back to the same state, just skip.
|
||||
// Could be clicking back from hashchange rather than a pushState.
|
||||
if (previousState.id === state.id) return
|
||||
|
||||
// Since state IDs always increase, we can deduce the navigation direction
|
||||
direction = previousState.id < state.id ? 'forward' : 'back'
|
||||
}
|
||||
|
||||
var cache = cacheMapping[state.id] || []
|
||||
var container = $(cache[0] || state.container), contents = cache[1]
|
||||
|
||||
if (container.length) {
|
||||
if (previousState) {
|
||||
// Cache current container before replacement and inform the
|
||||
// cache which direction the history shifted.
|
||||
cachePop(direction, previousState.id, cloneContents(container))
|
||||
}
|
||||
|
||||
var popstateEvent = $.Event('pjax:popstate', {
|
||||
state: state,
|
||||
direction: direction
|
||||
})
|
||||
container.trigger(popstateEvent)
|
||||
|
||||
var options = {
|
||||
id: state.id,
|
||||
url: state.url,
|
||||
container: container,
|
||||
push: false,
|
||||
fragment: state.fragment,
|
||||
timeout: state.timeout,
|
||||
scrollTo: false
|
||||
}
|
||||
|
||||
if (contents) {
|
||||
container.trigger('pjax:start', [null, options])
|
||||
|
||||
pjax.state = state
|
||||
if (state.title) document.title = state.title
|
||||
var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
|
||||
state: state,
|
||||
previousState: previousState
|
||||
})
|
||||
container.trigger(beforeReplaceEvent, [contents, options])
|
||||
container.html(contents)
|
||||
|
||||
container.trigger('pjax:end', [null, options])
|
||||
} else {
|
||||
pjax(options)
|
||||
}
|
||||
|
||||
// Force reflow/relayout before the browser tries to restore the
|
||||
// scroll position.
|
||||
container[0].offsetHeight
|
||||
} else {
|
||||
locationReplace(location.href)
|
||||
}
|
||||
}
|
||||
initialPop = false
|
||||
}
|
||||
|
||||
// Fallback version of main pjax function for browsers that don't
|
||||
// support pushState.
|
||||
//
|
||||
// Returns nothing since it retriggers a hard form submission.
|
||||
function fallbackPjax(options) {
|
||||
var url = $.isFunction(options.url) ? options.url() : options.url,
|
||||
method = options.type ? options.type.toUpperCase() : 'GET'
|
||||
|
||||
var form = $('<form>', {
|
||||
method: method === 'GET' ? 'GET' : 'POST',
|
||||
action: url,
|
||||
style: 'display:none'
|
||||
})
|
||||
|
||||
if (method !== 'GET' && method !== 'POST') {
|
||||
form.append($('<input>', {
|
||||
type: 'hidden',
|
||||
name: '_method',
|
||||
value: method.toLowerCase()
|
||||
}))
|
||||
}
|
||||
|
||||
var data = options.data
|
||||
if (typeof data === 'string') {
|
||||
$.each(data.split('&'), function(index, value) {
|
||||
var pair = value.split('=')
|
||||
form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
|
||||
})
|
||||
} else if ($.isArray(data)) {
|
||||
$.each(data, function(index, value) {
|
||||
form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
|
||||
})
|
||||
} else if (typeof data === 'object') {
|
||||
var key
|
||||
for (key in data)
|
||||
form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
|
||||
}
|
||||
|
||||
$(document.body).append(form)
|
||||
form.submit()
|
||||
}
|
||||
|
||||
// Internal: Abort an XmlHttpRequest if it hasn't been completed,
|
||||
// also removing its event handlers.
|
||||
function abortXHR(xhr) {
|
||||
if ( xhr && xhr.readyState < 4) {
|
||||
xhr.onreadystatechange = $.noop
|
||||
xhr.abort()
|
||||
}
|
||||
}
|
||||
|
||||
// Internal: Generate unique id for state object.
|
||||
//
|
||||
// Use a timestamp instead of a counter since ids should still be
|
||||
// unique across page loads.
|
||||
//
|
||||
// Returns Number.
|
||||
function uniqueId() {
|
||||
return (new Date).getTime()
|
||||
}
|
||||
|
||||
function cloneContents(container) {
|
||||
var cloned = container.clone()
|
||||
// Unmark script tags as already being eval'd so they can get executed again
|
||||
// when restored from cache. HAXX: Uses jQuery internal method.
|
||||
cloned.find('script').each(function(){
|
||||
if (!this.src) jQuery._data(this, 'globalEval', false)
|
||||
})
|
||||
return [container.selector, cloned.contents()]
|
||||
}
|
||||
|
||||
// Internal: Strip internal query params from parsed URL.
|
||||
//
|
||||
// Returns sanitized url.href String.
|
||||
function stripInternalParams(url) {
|
||||
url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '')
|
||||
return url.href.replace(/\?($|#)/, '$1')
|
||||
}
|
||||
|
||||
// Internal: Parse URL components and returns a Locationish object.
|
||||
//
|
||||
// url - String URL
|
||||
//
|
||||
// Returns HTMLAnchorElement that acts like Location.
|
||||
function parseURL(url) {
|
||||
var a = document.createElement('a')
|
||||
a.href = url
|
||||
return a
|
||||
}
|
||||
|
||||
// Internal: Return the `href` component of given URL object with the hash
|
||||
// portion removed.
|
||||
//
|
||||
// location - Location or HTMLAnchorElement
|
||||
//
|
||||
// Returns String
|
||||
function stripHash(location) {
|
||||
return location.href.replace(/#.*/, '')
|
||||
}
|
||||
|
||||
// Internal: Build options Object for arguments.
|
||||
//
|
||||
// For convenience the first parameter can be either the container or
|
||||
// the options object.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// optionsFor('#container')
|
||||
// // => {container: '#container'}
|
||||
//
|
||||
// optionsFor('#container', {push: true})
|
||||
// // => {container: '#container', push: true}
|
||||
//
|
||||
// optionsFor({container: '#container', push: true})
|
||||
// // => {container: '#container', push: true}
|
||||
//
|
||||
// Returns options Object.
|
||||
function optionsFor(container, options) {
|
||||
// Both container and options
|
||||
if ( container && options )
|
||||
options.container = container
|
||||
|
||||
// First argument is options Object
|
||||
else if ( $.isPlainObject(container) )
|
||||
options = container
|
||||
|
||||
// Only container
|
||||
else
|
||||
options = {container: container}
|
||||
|
||||
// Find and validate container
|
||||
if (options.container)
|
||||
options.container = findContainerFor(options.container)
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
// Internal: Find container element for a variety of inputs.
|
||||
//
|
||||
// Because we can't persist elements using the history API, we must be
|
||||
// able to find a String selector that will consistently find the Element.
|
||||
//
|
||||
// container - A selector String, jQuery object, or DOM Element.
|
||||
//
|
||||
// Returns a jQuery object whose context is `document` and has a selector.
|
||||
function findContainerFor(container) {
|
||||
container = $(container)
|
||||
|
||||
if ( !container.length ) {
|
||||
throw "no pjax container for " + container.selector
|
||||
} else if ( container.selector !== '' && container.context === document ) {
|
||||
return container
|
||||
} else if ( container.attr('id') ) {
|
||||
return $('#' + container.attr('id'))
|
||||
} else {
|
||||
throw "cant get selector for pjax container!"
|
||||
}
|
||||
}
|
||||
|
||||
// Internal: Filter and find all elements matching the selector.
|
||||
//
|
||||
// Where $.fn.find only matches descendants, findAll will test all the
|
||||
// top level elements in the jQuery object as well.
|
||||
//
|
||||
// elems - jQuery object of Elements
|
||||
// selector - String selector to match
|
||||
//
|
||||
// Returns a jQuery object.
|
||||
function findAll(elems, selector) {
|
||||
return elems.filter(selector).add(elems.find(selector));
|
||||
}
|
||||
|
||||
function parseHTML(html) {
|
||||
return $.parseHTML(html, document, true)
|
||||
}
|
||||
|
||||
// Internal: Extracts container and metadata from response.
|
||||
//
|
||||
// 1. Extracts X-PJAX-URL header if set
|
||||
// 2. Extracts inline <title> tags
|
||||
// 3. Builds response Element and extracts fragment if set
|
||||
//
|
||||
// data - String response data
|
||||
// xhr - XHR response
|
||||
// options - pjax options Object
|
||||
//
|
||||
// Returns an Object with url, title, and contents keys.
|
||||
function extractContainer(data, xhr, options) {
|
||||
var obj = {}, fullDocument = /<html/i.test(data)
|
||||
|
||||
// Prefer X-PJAX-URL header if it was set, otherwise fallback to
|
||||
// using the original requested url.
|
||||
var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
|
||||
obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl
|
||||
|
||||
// Attempt to parse response html into elements
|
||||
if (fullDocument) {
|
||||
var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))
|
||||
var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
|
||||
} else {
|
||||
var $head = $body = $(parseHTML(data))
|
||||
}
|
||||
|
||||
// If response data is empty, return fast
|
||||
if ($body.length === 0)
|
||||
return obj
|
||||
|
||||
// If there's a <title> tag in the header, use it as
|
||||
// the page's title.
|
||||
obj.title = findAll($head, 'title').last().text()
|
||||
|
||||
if (options.fragment) {
|
||||
// If they specified a fragment, look for it in the response
|
||||
// and pull it out.
|
||||
if (options.fragment === 'body') {
|
||||
var $fragment = $body
|
||||
} else {
|
||||
var $fragment = findAll($body, options.fragment).first()
|
||||
}
|
||||
|
||||
if ($fragment.length) {
|
||||
obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
|
||||
|
||||
// If there's no title, look for data-title and title attributes
|
||||
// on the fragment
|
||||
if (!obj.title)
|
||||
obj.title = $fragment.attr('title') || $fragment.data('title')
|
||||
}
|
||||
|
||||
} else if (!fullDocument) {
|
||||
obj.contents = $body
|
||||
}
|
||||
|
||||
// Clean up any <title> tags
|
||||
if (obj.contents) {
|
||||
// Remove any parent title elements
|
||||
obj.contents = obj.contents.not(function() { return $(this).is('title') })
|
||||
|
||||
// Then scrub any titles from their descendants
|
||||
obj.contents.find('title').remove()
|
||||
|
||||
// Gather all script[src] elements
|
||||
obj.scripts = findAll(obj.contents, 'script[src]').remove()
|
||||
obj.contents = obj.contents.not(obj.scripts)
|
||||
}
|
||||
|
||||
// Trim any whitespace off the title
|
||||
if (obj.title) obj.title = $.trim(obj.title)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// Load an execute scripts using standard script request.
|
||||
//
|
||||
// Avoids jQuery's traditional $.getScript which does a XHR request and
|
||||
// globalEval.
|
||||
//
|
||||
// scripts - jQuery object of script Elements
|
||||
//
|
||||
// Returns nothing.
|
||||
function executeScriptTags(scripts) {
|
||||
if (!scripts) return
|
||||
|
||||
var existingScripts = $('script[src]')
|
||||
|
||||
scripts.each(function() {
|
||||
var src = this.src
|
||||
var matchedScripts = existingScripts.filter(function() {
|
||||
return this.src === src
|
||||
})
|
||||
if (matchedScripts.length) return
|
||||
|
||||
var script = document.createElement('script')
|
||||
var type = $(this).attr('type')
|
||||
if (type) script.type = type
|
||||
script.src = $(this).attr('src')
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
// Internal: History DOM caching class.
|
||||
var cacheMapping = {}
|
||||
var cacheForwardStack = []
|
||||
var cacheBackStack = []
|
||||
|
||||
// Push previous state id and container contents into the history
|
||||
// cache. Should be called in conjunction with `pushState` to save the
|
||||
// previous container contents.
|
||||
//
|
||||
// id - State ID Number
|
||||
// value - DOM Element to cache
|
||||
//
|
||||
// Returns nothing.
|
||||
function cachePush(id, value) {
|
||||
cacheMapping[id] = value
|
||||
cacheBackStack.push(id)
|
||||
|
||||
// Remove all entries in forward history stack after pushing a new page.
|
||||
trimCacheStack(cacheForwardStack, 0)
|
||||
|
||||
// Trim back history stack to max cache length.
|
||||
trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
|
||||
}
|
||||
|
||||
// Shifts cache from directional history cache. Should be
|
||||
// called on `popstate` with the previous state id and container
|
||||
// contents.
|
||||
//
|
||||
// direction - "forward" or "back" String
|
||||
// id - State ID Number
|
||||
// value - DOM Element to cache
|
||||
//
|
||||
// Returns nothing.
|
||||
function cachePop(direction, id, value) {
|
||||
var pushStack, popStack
|
||||
cacheMapping[id] = value
|
||||
|
||||
if (direction === 'forward') {
|
||||
pushStack = cacheBackStack
|
||||
popStack = cacheForwardStack
|
||||
} else {
|
||||
pushStack = cacheForwardStack
|
||||
popStack = cacheBackStack
|
||||
}
|
||||
|
||||
pushStack.push(id)
|
||||
if (id = popStack.pop())
|
||||
delete cacheMapping[id]
|
||||
|
||||
// Trim whichever stack we just pushed to to max cache length.
|
||||
trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
|
||||
}
|
||||
|
||||
// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no
|
||||
// longer than the specified length, deleting cached DOM elements as necessary.
|
||||
//
|
||||
// stack - Array of state IDs
|
||||
// length - Maximum length to trim to
|
||||
//
|
||||
// Returns nothing.
|
||||
function trimCacheStack(stack, length) {
|
||||
while (stack.length > length)
|
||||
delete cacheMapping[stack.shift()]
|
||||
}
|
||||
|
||||
// Public: Find version identifier for the initial page load.
|
||||
//
|
||||
// Returns String version or undefined.
|
||||
function findVersion() {
|
||||
return $('meta').filter(function() {
|
||||
var name = $(this).attr('http-equiv')
|
||||
return name && name.toUpperCase() === 'X-PJAX-VERSION'
|
||||
}).attr('content')
|
||||
}
|
||||
|
||||
// Install pjax functions on $.pjax to enable pushState behavior.
|
||||
//
|
||||
// Does nothing if already enabled.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $.pjax.enable()
|
||||
//
|
||||
// Returns nothing.
|
||||
function enable() {
|
||||
$.fn.pjax = fnPjax
|
||||
$.pjax = pjax
|
||||
$.pjax.enable = $.noop
|
||||
$.pjax.disable = disable
|
||||
$.pjax.click = handleClick
|
||||
$.pjax.submit = handleSubmit
|
||||
$.pjax.reload = pjaxReload
|
||||
$.pjax.defaults = {
|
||||
timeout: 650,
|
||||
push: true,
|
||||
replace: false,
|
||||
type: 'GET',
|
||||
dataType: 'html',
|
||||
scrollTo: 0,
|
||||
maxCacheLength: 20,
|
||||
version: findVersion
|
||||
}
|
||||
$(window).on('popstate.pjax', onPjaxPopstate)
|
||||
}
|
||||
|
||||
// Disable pushState behavior.
|
||||
//
|
||||
// This is the case when a browser doesn't support pushState. It is
|
||||
// sometimes useful to disable pushState for debugging on a modern
|
||||
// browser.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// $.pjax.disable()
|
||||
//
|
||||
// Returns nothing.
|
||||
function disable() {
|
||||
$.fn.pjax = function() { return this }
|
||||
$.pjax = fallbackPjax
|
||||
$.pjax.enable = enable
|
||||
$.pjax.disable = $.noop
|
||||
$.pjax.click = $.noop
|
||||
$.pjax.submit = $.noop
|
||||
$.pjax.reload = function() { window.location.reload() }
|
||||
|
||||
$(window).off('popstate.pjax', onPjaxPopstate)
|
||||
}
|
||||
|
||||
|
||||
// Add the state property to jQuery's event object so we can use it in
|
||||
// $(window).bind('popstate')
|
||||
if ( $.inArray('state', $.event.props) < 0 )
|
||||
$.event.props.push('state')
|
||||
|
||||
// Is pjax supported by this browser?
|
||||
$.support.pjax =
|
||||
window.history && window.history.pushState && window.history.replaceState &&
|
||||
// pushState isn't reliable on iOS until 5.
|
||||
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
|
||||
|
||||
$.support.pjax ? enable() : disable()
|
||||
|
||||
})(jQuery);
|
4
node_modules/.gitignore
generated
vendored
Normal file
4
node_modules/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
9
package.json
Normal file
9
package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "humhub",
|
||||
"devDependencies": {
|
||||
"grunt-contrib-clean": "^1.0.0",
|
||||
"grunt-contrib-concat": "^1.0.0",
|
||||
"grunt-contrib-uglify": "^1.0.1",
|
||||
"grunt-contrib-watch": "^1.0.0"
|
||||
}
|
||||
}
|
@ -36,12 +36,14 @@ class AppAsset extends AssetBundle
|
||||
'js/jquery.loader.js',
|
||||
'js/desktop-notify-min.js',
|
||||
'js/desktop-notify-config.js',
|
||||
'js/jquery.pjax.js',
|
||||
'resources/at/jquery.caret.min.js',
|
||||
'resources/at/jquery.atwho.min.js',
|
||||
'resources/file/fileuploader.js',
|
||||
'resources/user/userpicker.js',
|
||||
'js/app.js',
|
||||
'js/dist/humhub.all.js'
|
||||
'js/dist/humhub.all.js',
|
||||
|
||||
];
|
||||
|
||||
//TODO if !debug use humhub.all.min.js
|
||||
|
@ -18,6 +18,9 @@ use Yii;
|
||||
class JSONResponse
|
||||
{
|
||||
|
||||
const STATE_CONFIRM = 0;
|
||||
const STATE_ERROR_APPLICATION = 1;
|
||||
const STATE_ERROR_VALIDATOIN = 2;
|
||||
/**
|
||||
* The resulting json array
|
||||
* @var type
|
||||
@ -29,10 +32,11 @@ class JSONResponse
|
||||
$result = [];
|
||||
}
|
||||
|
||||
public function error($errorMessage, $errorCode)
|
||||
public function error($errors, $errorTitle = null, $status = null)
|
||||
{
|
||||
$this->result['error'] = $errorMessage;
|
||||
$this->result['errorCode'] = $errorCode;
|
||||
$this->result['errorTitle'] = $errorTitle;
|
||||
$this->result['errors'] = $errors;
|
||||
$this->result['status'] = ($status != null && $status > 0) ? $status : self::STATE_ERROR_APPLICATION;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -42,6 +46,15 @@ class JSONResponse
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function confirm($content)
|
||||
{
|
||||
if($content != null) {
|
||||
$this->content($content);
|
||||
}
|
||||
$this->result['status'] = self::STATE_CONFIRM;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function data($key, $value)
|
||||
{
|
||||
if(!is_array($this->$result['data'])) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1505,7 +1505,11 @@ input:-moz-placeholder {
|
||||
color: @font4 !important;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
.help-block-error {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.help-block:not(.help-block-error) {
|
||||
color: @font5 !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user