├── renderers ├── rendererSkeleton.js ├── example │ └── helloWorldRenderer.js └── README.md ├── plugins ├── README.md ├── external │ ├── README.md │ ├── example │ │ └── helloWorldPlugin.js │ └── externalPluginSkeleton.js └── internal │ ├── README.md │ ├── example │ └── helloWorldPlugin.js │ └── internalPluginSkeleton.js ├── LICENSE └── README.md /renderers/rendererSkeleton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample renderer skeleton. 3 | * 4 | * @param {Object} instance Currently processed Handsontable instance. 5 | * @param {HTMLElement} td Currently rendered cell TD element. 6 | * @param {Number} row Row index. 7 | * @param {Number} col Column index. 8 | * @param {String|Number} prop Column index or property name. 9 | * @param {String} value Cell contents. 10 | * @param {Object} cellProperties Currently processed cell properties object, containing the cell's metadata. 11 | */ 12 | function rendererSkeleton(instance, td, row, col, prop, value, cellProperties) { 13 | 14 | // Sample operation on the cell. In this case, we modify the currently rendered cell's style property. 15 | if (row === 0 && col === 0) { 16 | td.style.fontWeight = 'bold'; 17 | } 18 | } -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | ## What is a plugin? 4 | Basically, a plugin is a class which extends the original functionality of Handsontable. 5 | 6 | ## Internal or external? 7 | If you wish to make your own Handsontable build, it's a good idea to choose the internal approach. 8 | This way you'll simply need to include a customized `handsontable.js` (or `handsontable.full.js`, [see the difference](https://github.com/handsontable/handsontable/tree/master/dist)) to your website. 9 | 10 | However, if you want to add a new functionality to the Handsontable without making your own build, and you don't mind linking more files to your site, you can go with the external approach. 11 | 12 | * [Internal plugin details](internal) 13 | * [External plugin details](external) 14 | 15 | Regardless of the chosen approach, you can access any plugin's instance by calling: 16 | ```js 17 | handsontableInstance.getPlugin('nameOfThePlugin'); 18 | ``` -------------------------------------------------------------------------------- /plugins/external/README.md: -------------------------------------------------------------------------------- 1 | ## External plugin approach 2 | * The plugin code doesn't need to be written in [ES6](http://www.ecma-international.org/ecma-262/6.0/), 3 | * Your class has to extend the [BasePlugin](https://github.com/handsontable/handsontable/blob/master/src/plugins/_base.js) class, 4 | * Your plugin files need to be included to your site, see the following example: 5 | ```html 6 | 7 | 8 | ``` 9 | 10 | ## Quick start step by step tutorial 11 | * Copy the [externalPluginSkeleton.js](externalPluginSkeleton.js) file and rename it to describe your plugin. Let's use `myPlugin.js` for this example. 12 | * Change the class name to fit the filename, in our case: 13 | ```js 14 | function MyPlugin(hotInstance) { 15 | ``` 16 | and in the method definitions: 17 | ```js 18 | // for example 19 | MyPlugin.prototype.isEnabled = function() { 20 | } 21 | ``` 22 | * Implement your functionality using the guidelines from the comments, 23 | * Include the `handsontable.full.js` and `myPlugin.js` files on your website, and you're good to go! 24 | ```html 25 | 26 | 27 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012-2014 Marcin Warpechowski 4 | Copyright (c) 2015 Handsoncode sp. z o.o. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | 'Software'), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /renderers/example/helloWorldRenderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hello world renderer. It renders "Hello World!" in a cell, when it's value equals "hello" and "Handsontable is awesome!", 3 | * when it's value is "handsontable". It also changes the cell's background color. 4 | * 5 | * @param {Object} instance Currently processed Handsontable instance. 6 | * @param {HTMLElement} td Currently rendered cell TD element. 7 | * @param {Number} row Row index. 8 | * @param {Number} col Column index. 9 | * @param {String|Number} prop Column index or property name. 10 | * @param {String} value Cell contents. 11 | * @param {Object} cellProperties Currently processed cell properties object, containing the cell's metadata. 12 | */ 13 | function helloWorldRenderer(instance, td, row, col, prop, value, cellProperties) { 14 | var changed = false; 15 | 16 | if (value && value.toString().toLowerCase() === 'hello') { 17 | td.textContent = 'Hello World!'; 18 | changed = true; 19 | 20 | } else if (value && value.toString().toLowerCase() === 'handsontable') { 21 | td.textContent = 'Handsontable is awesome!'; 22 | changed = true; 23 | 24 | } else { 25 | td.textContent = value; 26 | } 27 | 28 | if (changed) { 29 | td.style.background = '#E5EDEF'; 30 | } else { 31 | td.style.background = ''; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Handsontable Skeleton 2 | 3 | ## What is it? 4 | This repo contains the sample structures of components commonly used in Handsontable. 5 | 6 | Over the last years Handsontable has become more and more complex. Many developers started contributing to this project, too. 7 | If you plan to contribute, or just extend your local implemention's functionality, please use our unified templates, as they help us keep the library easy to maintain and extend. 8 | 9 | #### Currently available: 10 | * [Plugins](plugins) 11 | * [Internal plugins](plugins/internal/) 12 | * [External plugins](plugins/external/) 13 | * [Renderers](renderers) 14 | 15 | ## Why use it? 16 | It's much easier to start with an established structure than create new feautures from scratch. 17 | 18 | This skeleton will save your time and allow to focus on the logic behind the functionality instead of setting up the proper structure. 19 | 20 | ## Who made these? 21 | The Handsontable Skeleton was created by the core Handsontable team. 22 | 23 | ## The License 24 | Handsontable Skeleton is distributed under the [MIT License](LICENSE). 25 | 26 | ## Found a bug? 27 | [Add a new issue](https://github.com/handsontable/handsontable-skeleton/issues/new) or [make a pull request](https://github.com/handsontable/handsontable-skeleton/pulls). 28 | 29 | ## Contact 30 | Get in touch at hello@handsontable.com. 31 | -------------------------------------------------------------------------------- /renderers/README.md: -------------------------------------------------------------------------------- 1 | # Renderers 2 | 3 | ## What is a renderer? 4 | Renderer is basically a function run every time a cell is being rendered. 5 | 6 | ## Sample renderer step by step tutorial 7 | * Write a function based on the guidelines provided in the [rendererSkeleton.js](rendererSkeleton.js) file, 8 | * Define the renderer in your Handsontable configuration: 9 | 10 | To apply it to every cell in Handsontable: 11 | ```js 12 | var hot = new Handsontable(container1, { 13 | data: data, 14 | renderer: yourRendererFunction 15 | }); 16 | ``` 17 | 18 | To apply it only to certain columns: 19 | ```js 20 | // This setup would apply your custom renderer only to the second table column. 21 | 22 | var hot = new Handsontable(container1, { 23 | data: data, 24 | columns: [ 25 | {}, 26 | {renderer: yourRendererFunction} 27 | ] 28 | }); 29 | ``` 30 | 31 | To apply it to certain cells: 32 | ```js 33 | // This setup would apply your custom renderer only to the cell at coordinates (1,1). 34 | 35 | var hot = new Handsontable(container1, { 36 | data: data, 37 | cells: function(row, col, prop) { 38 | if (row === 1 && col === 1) { 39 | var cellProperties = {}; 40 | cellProperties.renderer = yourRendererFunction; 41 | 42 | return cellProperties; 43 | } 44 | } 45 | }); 46 | ``` 47 | 48 | To read more about custom renderers, see our documentation: http://docs.handsontable.com/demo-custom-renderers.html. 49 | -------------------------------------------------------------------------------- /plugins/internal/README.md: -------------------------------------------------------------------------------- 1 | # Internal plugin approach 2 | 3 | ## General guidelines 4 | * The plugin code should be written in [ES6](http://www.ecma-international.org/ecma-262/6.0/). The currently supported ES6 features are (listed in our documentation)[http://docs.handsontable.com/tutorial-seven-principles.html], 5 | * Your class has to extend the [BasePlugin](https://github.com/handsontable/handsontable/blob/master/src/plugins/_base.js) class, 6 | * Your plugin files need to be places under `src/plugins/nameOfYourPlugin/` directory to be properly recognized by our builder, 7 | * You need to make your own Handsontable build to incorporate your changes into the Handsontable source. [Learn how to do it in our documentation](http://docs.handsontable.com/tutorial-custom-build.html). 8 | 9 | ## Quick start step by step tutorial 10 | * Copy the [internalPluginSkeleton.js](internalPluginSkeleton.js) file and rename it to describe your plugin. Let's use `myPlugin.js` for this example. 11 | * Change the class name to fit the filename, in our case: 12 | ```js 13 | class MyPlugin extends BasePlugin 14 | ``` 15 | * Implement your functionality using the guidelines from the comments, 16 | * Put your plugin in the `src/plugins/myPlugin` Handsontable's subdirectory, 17 | * Open the terminal, navigate to your Handsontable location, 18 | * [Follow this steps to run your build](http://docs.handsontable.com/tutorial-custom-build.html#page-running) 19 | * Include the `handsontable.full.js` file on your website, and you're good to go! -------------------------------------------------------------------------------- /plugins/internal/example/helloWorldPlugin.js: -------------------------------------------------------------------------------- 1 | import BasePlugin from './../_base'; 2 | import {arrayEach} from './../../helpers/array'; 3 | import {registerPlugin} from './../../plugins'; 4 | 5 | /** 6 | * @plugin HelloWorldPlugin 7 | * 8 | * @description 9 | * Every time you type "Hello" in a cell, HelloWorldPlugins adds "World!" in the next cell. 10 | * Also, when you type "Handsontable", it adds "is awesome!" in the next cell. 11 | */ 12 | class HelloWorldPlugin extends BasePlugin { 13 | constructor(hotInstance) { 14 | super(hotInstance); 15 | 16 | /** 17 | * Array containing the vocabulary used in the plugin. 18 | * 19 | * @type {Array} 20 | */ 21 | this.vocabularyArray = []; 22 | } 23 | 24 | /** 25 | * Check if the plugin is enabled in the settings. 26 | */ 27 | isEnabled() { 28 | return !!this.hot.getSettings().helloWorldPlugin; 29 | } 30 | 31 | /** 32 | * Enable the plugin. 33 | */ 34 | enablePlugin() { 35 | this.vocabularyArray = [ 36 | ['Hello', 'World!'], 37 | ['Handsontable', 'is awesome!'] 38 | ]; 39 | 40 | this.addHook('afterChange', this.onAfterChange.bind(this)); 41 | 42 | super.enablePlugin(); 43 | } 44 | 45 | /** 46 | * Disable the plugin. 47 | */ 48 | disablePlugin() { 49 | this.vocabularyArray = []; 50 | 51 | super.disablePlugin(); 52 | } 53 | 54 | /** 55 | * Update the plugin. 56 | */ 57 | updatePlugin() { 58 | this.disablePlugin(); 59 | this.enablePlugin(); 60 | 61 | super.updatePlugin(); 62 | } 63 | 64 | /** 65 | * The afterChange hook callback. 66 | * 67 | * @param {Array} changes Array of changes. 68 | * @param {String} source Describes the source of the change. 69 | */ 70 | onAfterChange(changes, source) { 71 | 72 | // Check wheter the changes weren't blank or the hook wasn't triggered inside this callback. 73 | if (!changes || source === 'helloWorldPlugin') { 74 | return; 75 | } 76 | 77 | arrayEach(changes, function(change, i) { 78 | arrayEach(this.vocabularyArray, function(entry, j) { 79 | 80 | if (change[3] && change[3].toString().toLowerCase() === entry[0].toString().toLowerCase()) { 81 | this.hot.setDataAtCell(change[0], change[1] + 1, entry[1], 'helloWorldPlugin'); 82 | } 83 | 84 | }); 85 | }); 86 | } 87 | 88 | /** 89 | * Destroy the plugin. 90 | */ 91 | destroy() { 92 | super.destroy(); 93 | } 94 | } 95 | 96 | export {HelloWorldPlugin}; 97 | 98 | registerPlugin('helloWorldPlugin', HelloWorldPlugin); 99 | -------------------------------------------------------------------------------- /plugins/external/example/helloWorldPlugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @plugin HelloWorldPlugin 3 | * 4 | * @description 5 | * Every time you type "Hello" in a cell, HelloWorldPlugins adds "World!" in the next cell. 6 | * Also, when you type "Handsontable", it adds "is awesome!" in the next cell. 7 | * 8 | * @param hotInstance 9 | * @constructor 10 | */ 11 | function HelloWorldPlugin(hotInstance) { 12 | Handsontable.plugins.BasePlugin.call(this, hotInstance); 13 | this._superClass = Handsontable.plugins.BasePlugin; 14 | 15 | /** 16 | * Array containing the vocabulary used in the plugin. 17 | * 18 | * @type {Array} 19 | */ 20 | this.vocabularyArray = []; 21 | } 22 | 23 | HelloWorldPlugin.prototype = Object.create(Handsontable.plugins.BasePlugin.prototype, { 24 | constructor: { 25 | writable: true, 26 | configurable: true, 27 | value: HelloWorldPlugin 28 | } 29 | }); 30 | 31 | /** 32 | * Check if the plugin is enabled in the settings. 33 | */ 34 | HelloWorldPlugin.prototype.isEnabled = function() { 35 | return !!this.hot.getSettings().helloWorldPlugin; 36 | }; 37 | 38 | /** 39 | * Enable the plujgin. 40 | */ 41 | HelloWorldPlugin.prototype.enablePlugin = function() { 42 | this.vocabularyArray = [ 43 | ['Hello', 'World!'], 44 | ['Handsontable', 'is awesome!'] 45 | ]; 46 | 47 | this.addHook('afterChange', this.onAfterChange.bind(this)); 48 | 49 | this._superClass.prototype.enablePlugin.call(this); 50 | }; 51 | 52 | /** 53 | * Disable the plugin. 54 | */ 55 | HelloWorldPlugin.prototype.disablePlugin = function() { 56 | this.vocabularyArray = []; 57 | 58 | this._superClass.prototype.disablePlugin.call(this); 59 | }; 60 | 61 | /** 62 | * Update the plugin. 63 | */ 64 | HelloWorldPlugin.prototype.updatePlugin = function() { 65 | this.disablePlugin(); 66 | this.enablePlugin(); 67 | 68 | this._superClass.prototype.updatePlugin.call(this); 69 | }; 70 | 71 | /** 72 | * The afterChange hook callback. 73 | * 74 | * @param {Array} changes Array of changes. 75 | * @param {String} source Describes the source of the change. 76 | */ 77 | HelloWorldPlugin.prototype.onAfterChange = function(changes, source) { 78 | 79 | // Check wheter the changes weren't blank or the hook wasn't triggered inside this callback. 80 | if (!changes || source === 'helloWorldPlugin') { 81 | return; 82 | } 83 | 84 | var arrayEach = Handsontable.helper.arrayEach; 85 | var _this = this; 86 | 87 | arrayEach(changes, function(change, i) { 88 | arrayEach(_this.vocabularyArray, function(entry, j) { 89 | 90 | if (change[3] && change[3].toString().toLowerCase() === entry[0].toLowerCase()) { 91 | _this.hot.setDataAtCell(change[0], change[1] + 1, entry[1], 'helloWorldPlugin'); 92 | } 93 | 94 | }); 95 | }); 96 | }; 97 | 98 | /** 99 | * Destroy the plugin. 100 | */ 101 | HelloWorldPlugin.prototype.destroy = function() { 102 | this._superClass.prototype.destroy.call(this); 103 | }; 104 | 105 | Handsontable.plugins.registerPlugin('helloWorldPlugin', HelloWorldPlugin); -------------------------------------------------------------------------------- /plugins/internal/internalPluginSkeleton.js: -------------------------------------------------------------------------------- 1 | // You need to import the BasePlugin class in order to inherit from it. 2 | import BasePlugin from './../../_base'; 3 | import {registerPlugin} from './../../../plugins'; 4 | 5 | /** 6 | * @plugin InternalPluginSkeleton 7 | * Note: keep in mind, that Handsontable instance creates one instance of the plugin class. 8 | * 9 | * @description 10 | * Blank plugin template. It needs to inherit from the BasePlugin class. 11 | */ 12 | class InternalPluginSkeleton extends BasePlugin { 13 | 14 | // The argument passed to the constructor is the currently processed Handsontable instance object. 15 | constructor(hotInstance) { 16 | super(hotInstance); 17 | 18 | // Initialize all your public properties in the class' constructor. 19 | /** 20 | * yourProperty description. 21 | * 22 | * @type {String} 23 | */ 24 | this.yourProperty = ''; 25 | /** 26 | * anotherProperty description. 27 | * @type {Array} 28 | */ 29 | this.anotherProperty = []; 30 | } 31 | 32 | /** 33 | * Checks if the plugin is enabled in the settings. 34 | */ 35 | isEnabled() { 36 | return !!this.hot.getSettings().internalPluginSkeleton; 37 | } 38 | 39 | /** 40 | * The enablePlugin method is triggered on the beforeInit hook. It should contain your initial plugin setup, along with 41 | * the hook connections. 42 | * Note, that this method is run only if the statement in the isEnabled method is true. 43 | */ 44 | enablePlugin() { 45 | this.yourProperty = 'Your Value'; 46 | 47 | // Add all your plugin hooks here. It's a good idea to make use of the arrow functions to keep the context consistent. 48 | this.addHook('afterChange', (changes, source) => this.onAfterChange(changes, source)); 49 | 50 | // The super method assigns the this.enabled property to true, which can be later used to check if plugin is already enabled. 51 | super.enablePlugin(); 52 | } 53 | 54 | /** 55 | * The disablePlugin method is used to disable the plugin. Reset all of your classes properties to their default values here. 56 | */ 57 | disablePlugin() { 58 | this.yourProperty = ''; 59 | this.anotherProperty = []; 60 | 61 | // The super method takes care of clearing the hook connections and assigning the 'false' value to the 'this.enabled' property. 62 | super.disablePlugin(); 63 | } 64 | 65 | /** 66 | * The updatePlugin method is called on the afterUpdateSettings hook (unless the updateSettings method turned the plugin off). 67 | * It should contain all the stuff your plugin needs to do to work properly after the Handsontable instance settings were modified. 68 | */ 69 | updatePlugin() { 70 | 71 | // The updatePlugin method needs to contain all the code needed to properly re-enable the plugin. In most cases simply disabling and enabling the plugin should do the trick. 72 | this.disablePlugin(); 73 | this.enablePlugin(); 74 | 75 | super.updatePlugin(); 76 | } 77 | 78 | /** 79 | * The afterChange hook callback. 80 | * 81 | * @param {Array} changes Array of changes. 82 | * @param {String} source Describes the source of the change. 83 | */ 84 | onAfterChange(changes, source) { 85 | // afterChange callback goes here. 86 | } 87 | 88 | /** 89 | * The destroy method should de-assign all of your properties. 90 | */ 91 | destroy() { 92 | // The super method takes care of de-assigning the event callbacks, plugin hooks and clearing all the plugin properties. 93 | super.destroy(); 94 | } 95 | } 96 | 97 | export {InternalPluginSkeleton}; 98 | 99 | // You need to register your plugin in order to use it within Handsontable. 100 | registerPlugin('internalPluginSkeleton', InternalPluginSkeleton); 101 | -------------------------------------------------------------------------------- /plugins/external/externalPluginSkeleton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @plugin External plugin skeleton. 3 | * Note: keep in mind, that Handsontable instance creates one instance of the plugin class. 4 | * 5 | * @param hotInstance 6 | * @constructor 7 | */ 8 | function ExternalPluginSkeleton(hotInstance) { 9 | 10 | // Call the BasePlugin constructor. 11 | Handsontable.plugins.BasePlugin.call(this, hotInstance); 12 | 13 | this._superClass = Handsontable.plugins.BasePlugin; 14 | 15 | // Initialize all your public properties in the class' constructor. 16 | /** 17 | * yourProperty description. 18 | * 19 | * @type {String} 20 | */ 21 | this.yourProperty = ''; 22 | /** 23 | * anotherProperty description. 24 | * @type {Array} 25 | */ 26 | this.anotherProperty = []; 27 | } 28 | 29 | // Inherit the BasePlugin prototype. 30 | ExternalPluginSkeleton.prototype = Object.create(Handsontable.plugins.BasePlugin.prototype, { 31 | constructor: { 32 | writable: true, 33 | configurable: true, 34 | value: ExternalPluginSkeleton 35 | }, 36 | }); 37 | 38 | /** 39 | * Checks if the plugin is enabled in the settings. 40 | */ 41 | ExternalPluginSkeleton.prototype.isEnabled = function() { 42 | return !!this.hot.getSettings().externalPluginSkeleton; 43 | }; 44 | 45 | /** 46 | * The enablePlugin method is triggered on the beforeInit hook. It should contain your initial plugin setup, along with 47 | * the hook connections. 48 | * Note, that this method is run only if the statement in the isEnabled method is true. 49 | */ 50 | ExternalPluginSkeleton.prototype.enablePlugin = function() { 51 | this.yourProperty = 'Your Value'; 52 | 53 | // Add all your plugin hooks here. It's a good idea to make use of the arrow functions to keep the context consistent. 54 | this.addHook('afterChange', this.onAfterChange.bind(this)); 55 | 56 | // The super class' method assigns the this.enabled property to true, which can be later used to check if plugin is already enabled. 57 | this._superClass.prototype.enablePlugin.call(this); 58 | }; 59 | 60 | /** 61 | * The disablePlugin method is used to disable the plugin. Reset all of your classes properties to their default values here. 62 | */ 63 | ExternalPluginSkeleton.prototype.disablePlugin = function() { 64 | this.yourProperty = ''; 65 | this.anotherProperty = []; 66 | 67 | // The super class' method takes care of clearing the hook connections and assigning the 'false' value to the 'this.enabled' property. 68 | this._superClass.prototype.disablePlugin.call(this); 69 | }; 70 | 71 | /** 72 | * The updatePlugin method is called on the afterUpdateSettings hook (unless the updateSettings method turned the plugin off). 73 | * It should contain all the stuff your plugin needs to do to work properly after the Handsontable instance settings were modified. 74 | */ 75 | ExternalPluginSkeleton.prototype.updatePlugin = function() { 76 | 77 | // The updatePlugin method needs to contain all the code needed to properly re-enable the plugin. In most cases simply disabling and enabling the plugin should do the trick. 78 | this.disablePlugin(); 79 | this.enablePlugin(); 80 | 81 | this._superClass.prototype.updatePlugin.call(this); 82 | }; 83 | 84 | /** 85 | * The afterChange hook callback. 86 | * 87 | * @param {Array} changes Array of changes. 88 | * @param {String} source Describes the source of the change. 89 | */ 90 | ExternalPluginSkeleton.prototype.onAfterChange = function(changes, source) { 91 | // afterChange callback goes here. 92 | }; 93 | 94 | /** 95 | * The destroy method should de-assign all of your properties. 96 | */ 97 | ExternalPluginSkeleton.prototype.destroy = function() { 98 | // The super method takes care of de-assigning the event callbacks, plugin hooks and clearing all the plugin properties. 99 | this._superClass.prototype.destroy.call(this); 100 | }; 101 | 102 | // You need to register your plugin in order to use it within Handsontable. 103 | Handsontable.plugins.registerPlugin('externalPluginSkeleton', ExternalPluginSkeleton); --------------------------------------------------------------------------------