File Manager
Upload
Current Directory: /home/lartcid/public_html/journal.lartc.id
[Back]
..
[Open]
Hapus
Rename
.htaccess
[Edit]
Hapus
Rename
.well-known
[Open]
Hapus
Rename
README.md
[Edit]
Hapus
Rename
api
[Open]
Hapus
Rename
cache
[Open]
Hapus
Rename
cgi-bin
[Open]
Hapus
Rename
classes
[Open]
Hapus
Rename
config.TEMPLATE.inc.php
[Edit]
Hapus
Rename
config.inc.php
[Edit]
Hapus
Rename
controllers
[Open]
Hapus
Rename
cypress.json
[Edit]
Hapus
Rename
dbscripts
[Open]
Hapus
Rename
docs
[Open]
Hapus
Rename
error_log
[Edit]
Hapus
Rename
favicon.ico
[Edit]
Hapus
Rename
index.php
[Edit]
Hapus
Rename
js
[Open]
Hapus
Rename
lib
[Open]
Hapus
Rename
locale
[Open]
Hapus
Rename
mini.php
[Edit]
Hapus
Rename
pages
[Open]
Hapus
Rename
php.ini
[Edit]
Hapus
Rename
plugins
[Open]
Hapus
Rename
public
[Open]
Hapus
Rename
registry
[Open]
Hapus
Rename
scheduledTaskLogs
[Open]
Hapus
Rename
schemas
[Open]
Hapus
Rename
styles
[Open]
Hapus
Rename
templates
[Open]
Hapus
Rename
tools
[Open]
Hapus
Rename
Edit File
/** * @defgroup js_controllers_grid */ /** * @file js/controllers/grid/GridHandler.js * * Copyright (c) 2014-2021 Simon Fraser University * Copyright (c) 2000-2021 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class GridHandler * @ingroup js_controllers_grid * * @brief Grid row handler. */ (function($) { // Define the namespace. $.pkp.controllers.grid = $.pkp.controllers.grid || {}; /** * @constructor * * @extends $.pkp.classes.Handler * * @param {jQueryObject} $grid The grid this handler is * attached to. * @param {{features}} options Grid handler configuration. */ $.pkp.controllers.grid.GridHandler = function($grid, options) { this.parent($grid, options); // We give a chance for this handler to initialize // before we initialize its features. this.initialize(options); this.initFeatures_(options.features); }; $.pkp.classes.Helper.inherits($.pkp.controllers.grid.GridHandler, $.pkp.classes.Handler); // // Constants. // /** * Flag to be used to fetch all curent page grid rows. * @public * @type {Object} */ $.pkp.controllers.grid.GridHandler.FETCH_ALL_ROWS_ID = {}; // // Protected properties // /** * The selector for the grid body. * @protected * @type {?string} */ $.pkp.controllers.grid.GridHandler.prototype.bodySelector = null; /** * The URL to fetch a grid row. * @protected * @type {?string} */ $.pkp.controllers.grid.GridHandler.prototype.fetchRowUrl = null; /** * The URL to fetch all loaded grid rows. * @protected * @type {?string} */ $.pkp.controllers.grid.GridHandler.prototype.fetchRowsUrl = null; // // Private properties // /** * The id of the grid. * @private * @type {?string} */ $.pkp.controllers.grid.GridHandler.prototype.gridId_ = null; /** * The URL to fetch the entire grid. * @private * @type {?string} */ $.pkp.controllers.grid.GridHandler.prototype.fetchGridUrl_ = null; /** * This grid features. * @private * @type {Object} */ $.pkp.controllers.grid.GridHandler.prototype.features_ = null; /** * Fetch elements extra request parameters. * @private * @type {Object} */ $.pkp.controllers.grid.GridHandler.prototype.fetchExtraParams_ = null; // // Public methods // /** * Get fetch element extra request parameters. * @return {Object} Extra request parameters. */ $.pkp.controllers.grid.GridHandler.prototype.getFetchExtraParams = function() { return this.fetchExtraParams_; }; /** * Set fetch element extra request parameters. * @param {Object} extraParams Extra request parameters. */ $.pkp.controllers.grid.GridHandler.prototype.setFetchExtraParams = function(extraParams) { this.fetchExtraParams_ = extraParams; }; /** * Get the fetch row URL. * @return {?string} URL to the "fetch row" operation handler. */ $.pkp.controllers.grid.GridHandler.prototype.getFetchRowUrl = function() { return this.fetchRowUrl; }; /** * Get the fetch rows URL. * @return {?string} URL to the "fetch rows" operation handler. */ $.pkp.controllers.grid.GridHandler.prototype.getFetchRowsUrl = function() { return this.fetchRowsUrl; }; /** * Get all grid rows. * * @return {jQueryObject} The rows as a JQuery object. */ $.pkp.controllers.grid.GridHandler.prototype.getRows = function() { return $('.gridRow', this.getHtmlElement()).not('.gridRowDeleted'); }; /** * Get the id prefix of this grid. * @return {string} The ID prefix of this grid. */ $.pkp.controllers.grid.GridHandler.prototype.getGridIdPrefix = function() { return 'component-' + this.gridId_; }; /** * Get the id prefix of this grid row. * @return {string} The id prefix of this grid row. */ $.pkp.controllers.grid.GridHandler.prototype.getRowIdPrefix = function() { return this.getGridIdPrefix() + '-row-'; }; /** * Get the grid row by the passed data element id. * @param {number} rowDataId * @param {number=} opt_parentElementId * @return {jQueryObject} */ $.pkp.controllers.grid.GridHandler.prototype.getRowByDataId = function(rowDataId, opt_parentElementId) { return $('#' + this.getRowIdPrefix() + $.pkp.classes.Helper.escapeJQuerySelector(String(rowDataId)), this.getHtmlElement()); }; /** * Get the data element id of the passed grid row. * @param {jQueryObject} $gridRow The grid row JQuery object. * @return {string} The data element id of the passed grid row. */ $.pkp.controllers.grid.GridHandler.prototype.getRowDataId = function($gridRow) { var rowDataId; rowDataId = /** @type {string} */ ($gridRow.attr('id'). slice(this.getRowIdPrefix().length)); return rowDataId; }; /** * Get the parent grid row of the passed element, if any. * @param {jQueryObject} $element The element that is inside the row. * @return {jQueryObject} The element parent grid row. */ $.pkp.controllers.grid.GridHandler.prototype.getParentRow = function($element) { return $element.parents('.gridRow:first'); }; /** * Get the same type elements of the passed element. * @param {jQueryObject} $element The element to get the type from. * @return {jQueryObject} The grid elements with the same type * of the passed element. */ $.pkp.controllers.grid.GridHandler.prototype.getElementsByType = function($element) { if ($element.hasClass('gridRow')) { var $container = $element.parents('tbody:first'); return $('.gridRow', $container); } else { return null; } }; /** * Get the empty element based on the type of the passed element. * @param {jQueryObject} $element The element to get the type from. * @return {jQueryObject} The empty element. */ $.pkp.controllers.grid.GridHandler.prototype.getEmptyElement = function($element) { if ($element.hasClass('gridRow')) { // Return the rows empty element placeholder. var $container = $element.parents('tbody:first'); return $container.next('.empty'); } else { return null; } }; /** * Show/hide row actions. * * @param {HTMLElement} sourceElement The element that * issued the event. * @param {Event} event The triggering event. */ $.pkp.controllers.grid.GridHandler.prototype.toggleRowActions = function(sourceElement, event) { // Don't follow the link event.preventDefault(); // Toggle the extras link class. $(sourceElement).toggleClass('show_extras'); $(sourceElement).toggleClass('hide_extras'); // Toggle the row actions. var $controlRow = $(sourceElement).parents('tr').next('.row_controls'); this.applyToggleRowActionEffect_($controlRow); }; /** * Hide all visible row controls. */ $.pkp.controllers.grid.GridHandler.prototype.hideAllVisibleRowActions = function() { this.getHtmlElement().find('a.hide_extras').click(); }; /** * Hide row actions div container. */ $.pkp.controllers.grid.GridHandler.prototype.hideRowActionsDiv = function() { var $rowActionDivs, index, limit, $div; $rowActionDivs = $('.gridRow div.row_actions', this.getHtmlElement()); $rowActionDivs.hide(); // FIXME: Hack to correctly align the first column cell content after // hiding the row actions div. for (index = 0, limit = $rowActionDivs.length; index < limit; index++) { $div = $($rowActionDivs[index]); } }; /** * Show row actions div container. */ $.pkp.controllers.grid.GridHandler.prototype.showRowActionsDiv = function() { var $rowActionDivs, index, limit, $div; $rowActionDivs = $('.gridRow div.row_actions', this.getHtmlElement()); $rowActionDivs.show(); }; /** * Enable/disable all link actions inside this grid. * @param {boolean} enable Enable/disable flag. * @param {jQueryObject} $linkElements Link elements JQuery object. */ $.pkp.controllers.grid.GridHandler.prototype.changeLinkActionsState = function(enable, $linkElements) { if ($linkElements === undefined) { $linkElements = $('.pkp_controllers_linkAction', this.getHtmlElement()); } $linkElements.each(function() { /** {$.pkp.controllers.LinkActionHandler} */ var linkHandler; linkHandler = $.pkp.classes.Handler.getHandler($(this)); if (enable) { linkHandler.enableLink(); } else { linkHandler.disableLink(); } }); }; /** * Re-sequence all grid rows based on the passed sequence map. * @param {Array} sequenceMap A sequence array with the row id or * row data id as value. */ $.pkp.controllers.grid.GridHandler.prototype.resequenceRows = function(sequenceMap) { var id, index, $row; if (!sequenceMap) { return; } for (index in sequenceMap) { id = sequenceMap[index]; $row = $('#' + $.pkp.classes.Helper.escapeJQuerySelector(String(id))); if ($row.length == 0) { $row = this.getRowByDataId(id); } if ($row.length == 0) { throw new Error('Row with id ' + id + ' not found!'); } this.addElement($row); } this.updateControlRowsPosition(); this.callFeaturesHook('resequenceRows', sequenceMap); }; /** * Move all grid control rows to their correct position, * below of each correspondent data grid row. */ $.pkp.controllers.grid.GridHandler.prototype.updateControlRowsPosition = function() { var $rows, index, limit, $row, $controlRow; $rows = this.getRows(); for (index = 0, limit = $rows.length; index < limit; index++) { $row = $($rows[index]); $controlRow = this.getControlRowByGridRow($row); if ($controlRow.length > 0) { $controlRow.insertAfter($row); } } }; /** * Inserts or replaces a grid element. * @param {string|jQueryObject} elementContent The new mark-up of the element. * @param {boolean=} opt_prepend Prepend the new row instead of append it? */ $.pkp.controllers.grid.GridHandler.prototype.insertOrReplaceElement = function(elementContent, opt_prepend) { var $newElement, newElementId, $grid, $existingElement; // Parse the HTML returned from the server. $newElement = $(elementContent); newElementId = $newElement.attr('id'); // Does the element exist already? $grid = this.getHtmlElement(); $existingElement = newElementId ? $grid.find('#' + $.pkp.classes.Helper.escapeJQuerySelector( /** @type {string} */ (newElementId)) ) : null; if ($existingElement !== null && $existingElement.length > 1) { throw new Error('There were ' + $existingElement.length + ' rather than 0 or 1 elements to be replaced!'); } if (!this.hasSameNumOfColumns($newElement)) { // Redraw the whole grid so new columns // get added/removed to match element. $.get(this.fetchGridUrl_, null, this.callbackWrapper(this.replaceGridResponseHandler_), 'json'); } else { if ($existingElement !== null && $existingElement.length === 1) { // Update element. this.replaceElement($existingElement, $newElement); } else { // Insert row. this.addElement($newElement, null, opt_prepend); } // Refresh row action event binding. this.activateRowActions_(); } }; /** * Delete a grid element. * @param {jQueryObject} $element The element to be deleted. * @param {boolean=} opt_noFadeOut Whether the item deletion * will use the fade out effect or not. */ $.pkp.controllers.grid.GridHandler.prototype.deleteElement = function($element, opt_noFadeOut) { var lastElement, $emptyElement, deleteFunction, self; // Check whether we really only match one element. if ($element.length !== 1) { throw new Error('There were ' + $element.length + ' rather than 1 element to delete!'); } // Flag this element as deleted, so getRows() // will return only existing rows from now on. $element.addClass('gridRowDeleted'); // Check whether this is the last row. lastElement = false; if (this.getElementsByType($element).length == 1) { lastElement = true; } // Remove the controls row, if any. if ($element.hasClass('gridRow')) { this.deleteControlsRow_($element); } $emptyElement = this.getEmptyElement($element); self = this; deleteFunction = function() { self.unbindPartial($element); $element.remove(); if (lastElement) { $emptyElement.fadeIn(100); } }; if (opt_noFadeOut != undefined && opt_noFadeOut) { deleteFunction(); } else { $element.fadeOut(500, deleteFunction); } }; // // Protected methods // /** * Set data and execute operations to initialize. * * @protected * * @param {Object} options Grid options. */ $.pkp.controllers.grid.GridHandler.prototype.initialize = function(options) { var $searchLink; // Bind the handler for the "elements changed" event. this.bind('dataChanged', this.refreshGridHandler); // Bind the handler for the "add new row" event. this.bind('addRow', this.addRowHandler_); // Handle grid filter events. this.bind('formSubmitted', this.refreshGridWithFilterHandler_); // Save the ID of this grid. this.gridId_ = options.gridId; // Save the URL to fetch a row. this.fetchRowUrl = options.fetchRowUrl; // Save the URL to fetch all rows. this.fetchRowsUrl = options.fetchRowsUrl; // Save the URL to fetch the entire grid this.fetchGridUrl_ = options.fetchGridUrl; // Save the selector for the grid body. if ($('div.scrollable', this.getHtmlElement()).length > 0) { this.bodySelector = 'div.scrollable table'; } else { this.bodySelector = options.bodySelector; } // Show/hide row action feature. this.activateRowActions_(); this.setFetchExtraParams({}); // Search control. this.getHtmlElement().find('.pkp_form').hide(); $searchLink = this.getHtmlElement(). find('.pkp_linkaction_search'); if ($searchLink.length !== 0) { $searchLink.click( this.callbackWrapper(function() { this.getHtmlElement().find('.pkp_form').toggle(); $searchLink.toggleClass('is_open'); })); } else { // This grid doesn't have an expand/collapse control. If there is // a form, expand it. this.getHtmlElement().find('.pkp_form').toggle(); } this.trigger('gridInitialized'); }; /** * Call features hooks. * * @protected * * @param {string} hookName The name of the hook. * @param {Array|jQueryObject|Object|number|boolean=} opt_args * The arguments array. */ $.pkp.controllers.grid.GridHandler.prototype.callFeaturesHook = function(hookName, opt_args) { var featureName; if (!$.isArray(opt_args)) { opt_args = [opt_args]; } for (featureName in this.features_) { this.features_[featureName][hookName]. apply(this.features_[featureName], opt_args); } }; /** * Refresh either a single row of the grid or the whole grid. * * @protected * * @param {HTMLElement} sourceElement The element that * issued the event. * @param {Event} event The triggering event. * @param {number|Object=} opt_elementId The id of a data element that was * updated, added or deleted. If not given then the whole grid * will be refreshed. * @param {Boolean=} opt_fetchedAlready Flag that subclasses can send * telling that a fetch operation was already handled there. */ $.pkp.controllers.grid.GridHandler.prototype.refreshGridHandler = function(sourceElement, event, opt_elementId, opt_fetchedAlready) { var params; this.callFeaturesHook('refreshGrid', opt_elementId); params = this.getFetchExtraParams(); // Check if subclasses already handled the fetch of new elements. if (!opt_fetchedAlready) { if (opt_elementId) { if (opt_elementId == $.pkp.controllers.grid.GridHandler.FETCH_ALL_ROWS_ID) { $.get(this.fetchRowsUrl, params, this.callbackWrapper(this.replaceElementResponseHandler), 'json'); } else { params.rowId = opt_elementId; // Retrieve a single row from the server. $.get(this.fetchRowUrl, params, this.callbackWrapper(this.replaceElementResponseHandler), 'json'); } } else { // Retrieve the whole grid from the server. $.get(this.fetchGridUrl_, params, this.callbackWrapper(this.replaceGridResponseHandler_), 'json'); } } // Let the calling context (page?) know that the grids are being redrawn. this.trigger('gridRefreshRequested'); this.publishChangeEvents(); }; /** * Add a new row to the grid. * * @protected * * @param {jQueryObject} $newRow The new row to append. * @param {jQueryObject=} opt_$gridBody The tbody container element. * @param {boolean=} opt_prepend Prepend the element instead of append it? */ $.pkp.controllers.grid.GridHandler.prototype.addElement = function($newRow, opt_$gridBody, opt_prepend) { if (opt_$gridBody === undefined || opt_$gridBody === null) { opt_$gridBody = this.getHtmlElement().find(this.bodySelector); } // Add the new element. if (opt_prepend != undefined && opt_prepend) { opt_$gridBody.prepend($newRow); } else { opt_$gridBody.append($newRow); } // Hide the empty placeholder. var $emptyElement = this.getEmptyElement($newRow); if ($emptyElement) { $emptyElement.hide(); } this.callFeaturesHook('addElement', $newRow); }; /** * Update an existing element using the passed new element content. * * @protected * * @param {jQueryObject} $existingElement The element that is already * in grid. * @param {jQueryObject} $newElement The element with new content. */ $.pkp.controllers.grid.GridHandler.prototype.replaceElement = function($existingElement, $newElement) { if ($newElement.hasClass('gridRow')) { this.deleteControlsRow_($existingElement); } this.replacePartialWith($newElement, $existingElement); this.callFeaturesHook('replaceElement', $newElement); }; /** * Does the passed row have a different number of columns than the * existing grid? * * @protected * * @param {jQueryObject} $row The row to be checked against grid columns. * @param {Boolean=} opt_checkColSpan Will get the number of row columns * by column span. * @return {boolean} Whether it has the same number of grid columns * or not. */ $.pkp.controllers.grid.GridHandler.prototype.hasSameNumOfColumns = function($row, opt_checkColSpan) { var $grid, numColumns, $tdElements, numCellsInNewRow; $grid = this.getHtmlElement(); numColumns = $grid.find('th').length; $tdElements = $row.first().find('td'); if (opt_checkColSpan) { numCellsInNewRow = $tdElements.attr('colspan'); } else { numCellsInNewRow = $tdElements.length; } return (numColumns == numCellsInNewRow); }; /** * Callback to insert, remove or replace a row after an * element has been inserted, update or deleted. * * @protected * * @param {Object} ajaxContext The AJAX request context. * @param {Object} jsonData A parsed JSON response object. * @return {boolean|undefined} Return false when no replace action is taken. */ $.pkp.controllers.grid.GridHandler.prototype.replaceElementResponseHandler = function(ajaxContext, jsonData) { var elementId, $element, handledJsonData, castJsonData, $responseElement, $responseRow, $responseControlRow, $responseRows, $responseRowsControls, index, limit; handledJsonData = this.handleJson(jsonData); if (handledJsonData !== false) { if (handledJsonData.elementNotFound) { // The server reported that this element no // longer exists in the database so let's // delete it. elementId = handledJsonData.elementNotFound; $element = this.getRowByDataId(elementId); // Sometimes we get a delete event before the // element has actually been inserted (e.g. when deleting // elements due to a cancel action or similar). if ($element.length > 0) { this.deleteElement($element); } } else { // The server returned mark-up to replace // or insert the row. $responseElement = $(handledJsonData.content); if ($responseElement.filter("tr:not('.row_controls')").length > 1) { $responseRows = $responseElement.filter('tr.gridRow'); $responseRowsControls = $responseElement.filter('tr.row_controls'); for (index = 0, limit = $responseRows.length; index < limit; index++) { $responseRow = $($responseRows[index]); $responseControlRow = this.getControlRowByGridRow($responseRow, $responseRowsControls); this.insertOrReplaceElement($responseRow.add($responseControlRow)); } } else { this.insertOrReplaceElement(handledJsonData.content); } castJsonData = /** @type {{sequenceMap: Array}} */ (handledJsonData); this.resequenceRows(castJsonData.sequenceMap); } } this.callFeaturesHook('replaceElementResponseHandler', handledJsonData); }; // // Private methods // /** * Refresh the grid after its filter has changed. * * @private * * @param {$.pkp.controllers.form.ClientFormHandler} filterForm * The filter form. * @param {Event} event A "formSubmitted" event. * @param {string} filterData Serialized filter data. */ $.pkp.controllers.grid.GridHandler.prototype.refreshGridWithFilterHandler_ = function(filterForm, event, filterData) { // Retrieve the grid from the server and add the // filter data as form data. $.post(this.fetchGridUrl_, filterData, this.callbackWrapper(this.replaceGridResponseHandler_), 'json'); }; /** * Add a new row to the grid. * * @private * * @param {HTMLElement} sourceElement The element that * issued the event. * @param {Event} event The triggering event. * @param {Object} params The request parameters to use to generate * the new row. */ $.pkp.controllers.grid.GridHandler.prototype.addRowHandler_ = function(sourceElement, event, params) { // Retrieve a single new row from the server. $.get(this.fetchRowUrl, params, this.callbackWrapper(this.replaceElementResponseHandler), 'json'); }; /** * Callback to replace a grid's content. * * @private * * @param {Object} ajaxContext The AJAX request context. * @param {Object} jsonData A parsed JSON response object. */ $.pkp.controllers.grid.GridHandler.prototype.replaceGridResponseHandler_ = function(ajaxContext, jsonData) { var handledJsonData, $grid, $gridParent, $newGrid, isFilterVisible; handledJsonData = this.handleJson(jsonData); if (handledJsonData !== false) { // Get the grid that we're updating $grid = this.getHtmlElement(); $gridParent = $grid.parent(); isFilterVisible = $grid.find('.filter').is(':visible'); // Replace the grid content this.replaceWith(handledJsonData.content); // Update the html element of this handler. $newGrid = $('div[id^="' + this.getGridIdPrefix() + '"]', $gridParent); this.setHtmlElement($newGrid); // Refresh row action event binding. this.activateRowActions_(); if (isFilterVisible) { // Open search control again. $newGrid.find('.pkp_linkaction_search').click(); } } }; /** * Helper that deletes the row of controls (if present). * * @private * * @param {jQueryObject} $row The row whose matching control row should be * deleted. */ $.pkp.controllers.grid.GridHandler.prototype.deleteControlsRow_ = function($row) { var $controlRow = $('#' + $.pkp.classes.Helper.escapeJQuerySelector( /** @type {string} */ ($row.attr('id'))) + '-control-row', this.getHtmlElement()); if ($controlRow.is('tr') && $controlRow.hasClass('row_controls')) { this.unbindPartial($controlRow); $controlRow.remove(); } }; /** * Get the control row for the passed the grid row. * * @param {jQueryObject} $gridRow The grid row JQuery object. * @param {jQueryObject=} opt_$context Optional context to get * the control row from. * @return {jQueryObject} The control row JQuery object. */ $.pkp.controllers.grid.GridHandler.prototype.getControlRowByGridRow = function($gridRow, opt_$context) { var rowId, controlRowId, $context; if (opt_$context === undefined || opt_$context === null) { $context = this.getHtmlElement().find('tr'); } else { $context = opt_$context; } rowId = $gridRow.attr('id'); controlRowId = rowId + '-control-row'; return $context.filter('#' + $.pkp.classes.Helper.escapeJQuerySelector(controlRowId)); }; /** * Helper that attaches any action handlers related to rows. * * @private */ $.pkp.controllers.grid.GridHandler.prototype.activateRowActions_ = function() { var $grid = this.getHtmlElement(), $gridRows = this.getHtmlElement().find('tr.gridRow').not('.category'); $grid.find('a.show_extras').unbind('click').bind('click', this.callbackWrapper(this.toggleRowActions)); }; /** * Apply the effect for hide/show row actions. * * @private * * @param {jQueryObject} $controlRow The control row JQuery object. */ $.pkp.controllers.grid.GridHandler.prototype.applyToggleRowActionEffect_ = function($controlRow) { var $row; $row = $controlRow.prev().find('td:not(.indent_row)'); $row = $row.add($controlRow.prev()); $controlRow.toggle(); }; /** * Add a grid feature. * * @private * * @param {string} id Feature id. * @param {$.pkp.classes.features.Feature} $feature The grid * feature to be added. */ $.pkp.controllers.grid.GridHandler.prototype.addFeature_ = function(id, $feature) { if (!this.features_) { this.features_ = []; } this.features_[id] = $feature; }; /** * Add grid features. * * @private * * @param {Array.<{JSClass, options}>} features The features options array. */ $.pkp.controllers.grid.GridHandler.prototype.initFeatures_ = function(features) { var id, $feature, jsClass; for (id in features) { // Only initiate features that have a js handler. jsClass = features[id].JSClass; if (jsClass === null) { continue; } $feature = /** @type {$.pkp.classes.features.Feature} */ ($.pkp.classes.Helper.objectFactory( jsClass, [this, features[id].options])); this.addFeature_(id, $feature); this.features_[id].init(); } }; }(jQuery));
Simpan