{"id":16994938,"url":"https://github.com/jackadams/meteor-editable-text","last_synced_at":"2025-03-22T15:31:05.638Z","repository":{"id":25073437,"uuid":"28493800","full_name":"JackAdams/meteor-editable-text","owner":"JackAdams","description":"Drop-in editable text widget for meteor","archived":false,"fork":false,"pushed_at":"2022-11-22T07:58:31.000Z","size":215,"stargazers_count":59,"open_issues_count":12,"forks_count":14,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-18T13:19:16.200Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://editable-text-demo.taonova.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JackAdams.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-25T23:37:09.000Z","updated_at":"2023-06-19T00:36:03.000Z","dependencies_parsed_at":"2023-01-14T02:05:10.283Z","dependency_job_id":null,"html_url":"https://github.com/JackAdams/meteor-editable-text","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JackAdams%2Fmeteor-editable-text","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JackAdams%2Fmeteor-editable-text/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JackAdams%2Fmeteor-editable-text/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JackAdams%2Fmeteor-editable-text/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JackAdams","download_url":"https://codeload.github.com/JackAdams/meteor-editable-text/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244978556,"owners_count":20541871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T03:47:07.478Z","updated_at":"2025-03-22T15:31:05.193Z","avatar_url":"https://github.com/JackAdams.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Editable Text for Meteor\n------------------------\n\nThis package provides a widget for rendering the fields of documents as editable text.\n\nExample app: [http://editable-text-demo.taonova.com](http://editable-text-demo.taonova.com) (also using collection2, accounts and transactions)\n\nExample app repo: [https://github.com/JackAdams/editable-text-demo](https://github.com/JackAdams/editable-text-demo)\n\n#### Quick Start\n\n    meteor add babrahams:editable-text\n\nYou can then drop an editable text widget into any Blaze template as follows:\n\n    {{\u003e editableText collection=\"posts\" field=\"author\"}}\n    \nwhere \"posts\" is the name of the actual mongo collection (not the name of a Meteor `Mongo.Collection` instance) and \"author\" is the name of a document field for the `posts` collection (`author.firstName` would also work as the field name).\n\n`collection` and `field` are the only mandatory parameters.\n\nNote: The widget assumes that the data context is that of a single document from the `posts` collection (with the _id value included).\n\nYou can also set the data context explicitly as follows:\n\n    {{\u003e editableText context=singlePostDocument collection=\"posts\" field=\"author\"}}\n\nwhere `singlePostDocument` can be a single post document already set in the current context, or provided by a template helper from the template that the widget was dropped into.\n\n(You can use `document`, `doc`, `object`, `obj`, `data` or `dataContext` instead of `context` - go with whichever you prefer.)\n\n#### API\n\nThis package exposes the symbols `EditableText` and `sanitizeHtml`. `sanitizeHtml` would only be used if you have very specific requirements when using a wysiwyg package and can be ignored for most apps. `EditableText` is used for setting app-wide config for the `{{\u003e editableText ...}}` widget as shown below.  \n\n#### Configuration\n\nYou can change the global behaviour of the widget by setting certain properties of `EditableText`.\n\n`EditableText.saveOnFocusout=false` will mean that the `focusout` event will not save text that is being edited (default is `EditableText.saveOnFocusout=true`)\n\n`EditableText.trustHtml=true` will mean that HTML entered in `input` and `textarea` fields is rendered as HTML (default is `EditableText.trustHTML=false`) - useful if you want newlines from textareas automatically represented as `\u003cbr /\u003e` tags\n\nSet several config properties at once using `EditableText.config({saveOnFocusout: false, trustHtml: true});`. Config properties that only have an effect on the client are: `saveOnFocusout`, `trustHtml`, and `useMethods`. Config properties that need to be set on both client and server are: `userCanEdit`, `useTransactions`, `maximumImageSize`, `allowedHtml`.  Server only: `clientControlsTransactions`.\n\n#### Options\n\nThere are a number of parameters you can pass to the widget that affect its behaviour:\n\n`acceptEmpty=true` will accept a value of `\"\"` for the field (by default, the widget won't make an update if an empty input value is entered)\n\n`unsetEmpty=true` will accept a value of `\"\"` and `$unset` the field (this overrides `acceptEmpty`)\n\n`removeEmpty=true` will remove the whole document from the database if the field value is set to `\"\"` (this trumps `acceptEmpty=true`!!)\n\n`textarea=true` will make the widget input field a textarea element (by default, it's an `\u003cinput type=\"text\" /\u003e`)\n\n`wysiwyg=true` will make the widget a wysiwyg editor (which is, at present, completely uncustomizable -- what you see is what you get! :-)). You'll need to `meteor add babrahams:editable-text-wysiwyg-bootstrap-3` or this `wysiwyg=true` will have no apparent effect and the editing widget will fall back to a textarea (with the difference being that HTML strings will be displayed as actual HTML not as a string showing the markup, so be careful with this). **Note:** When using content created using the wysiwyg editor in non-editable fields and other templates you will need to use triple curly braces like `{{{postText}}}`.\n\n`autoInsert=true` will let you supply a data context without an `_id` field and the widget will create a document using all the fields of the data context\n\n`beforeInsert=\"addTimestampToDocBeforeInsert\"` will call `addTimestampToDocBeforeInsert(documentToBeInserted, Collection)`, with `this` as the data that the `editableText` widget was initialized with, immediately before an auto insert. Return a modified document to have that inserted. Return `false` (not just any falsey value) to cancel the insert.\n\n`afterInsert=\"callbackFunction\"` will call `callbackFunction(newlyInsertedDocument, Collection)`, with `this` as the data that the `editableText` widget was initialized with, immediately after an auto insert\n\n`beforeUpdate=\"coerceTypeToDateBeforeUpdate\"`, will call `coerceTypeToDateBeforeUpdate(doc, Collection, newValue, modifier)`, with `this` as the data that the `editableText` widget was initialized with. `beforeUpdate` callbacks are special cased to be called with the `newValue` and `modifier` arguments. Also, for `beforeUpdate` callbacks only, if the callback function returns a value, that will replace the `newValue` that the user entered, **unless** the callback returns an object with `$set`, `$addToSet` or `$push` as one of its keys -- in this case, it will be assumed that it is overwriting the whole modifier, not just the `newValue`. See the examples below.\n\nOther available callback function hooks are `afterUpdate`, `beforeRemove`, `afterRemove` -- they each receive the document and Collection as their parameters and have the full widget data as `this`.  Returning `false` (not just any falsey value) from any of the 'before' callback functions will cause the action to be cancelled.\n\n`onStartEditing` and `onStopEditing` callbacks are called with `this` as the data that the `editableText` widget was initialized with and the document being edited as the only parameter. (The same is true of `onShowToolbar` and `onHideToolbar` if the `babrahams:editable-text-wysiwyg-bootstrap-3` package is added.)\n\nFor all callbacks, the values of the parameters must be the (string) names of functions, not the functions themselves. These functions have to be registered as follows, using `EditableText.registerCallbacks`:\n\n    EditableText.registerCallbacks({\n      addTimestampToDocBeforeInsert : function (doc) {\n        return _.extend(doc, {timestamp: Date.now()});\n      },\n\t  coerceTypeToDateBeforeUpdate : function (doc, Collection, newValue, modifier) {\n\t    return new Date(newValue);\n\t  },\n\t  checkForExpletivesBeforeUpdate : function (doc, Collection, newValue, modifier) {\n\t    var expletives = ['assorted', 'bad', 'words'];\n\t\tvar containsExpletives = !!_.find(expletives, function (expletive) {\n\t\t  return newValue.indexOf(expletive) \u003e -1;\n\t\t});\n\t\t// modifier is already of the form { $set: {text: newValue}}\n\t\tmodifier[\"$set\"].containsExpletives = containsExpletives;\n\t\t}\n\t\treturn modifier;\n\t  }\n    });\n    \nThese would then be applied by passing the parameter `beforeInsert='addTimestampToDocBeforeInsert'` (when initializing a widget that has also been passed `autoInsert=true`), `beforeUpdate='coerceTypeToDateBeforeUpdate'`, etc.\n\nNotice that returning a modified _document_ in a `beforeInsert` function will mean that this is the version of the document that will be inserted into the db, while returning a modified _value_ (usually a string) from a `beforeUpdate` function will mean that the modified value is used for the db update (good for custom validations).\n\n`eventType=\"dblclick\"` will make the text become editable only when double clicked (only event types supported are `\"click\"`, `\"dblclick\"`, `\"mousedown\"`) -- the default is `\"click\"`\n\n`type=\"number\"` will mean the value entered is stored as a `NumberInt` value in mongo (the default is `type=\"string\"`)\n\n`class=\"text-class\"` will change the class attribute of the `span` element wrapping the text that can be edited\n\n`inputClass=\"input-class\"` will change the class attribute of the `input` element once the text is being edited\n\n`style=dynamicStyle` can be used if you need to have more dynamic control over the style of the editable text (use a template helper to give the `dynamicStyle`) e.g.\n    \n    dynamicStyle : function() {\n      return 'color:' + Session.get('currentColor') + ';';\n    } \n\n`inputStyle=dynamicInputStyle` same as above, but for the `input` element when editing text\n\n`substitute='\u003ci class=\"fa fa-pencil\"\u003e\u003c/i\u003e'` will let you put something in as a substitute for the editable text if the field value is `''`\n\n`title=\"This is editable text\"` changes the title attribute on editable text (default is 'Click to edit')\n\n`userCanEdit=userCanEdit` is a way to tell the widget whether the current user can edit the text or only view it (using a template helper) e.g.\n    \n    userCanEdit : function() {\n      return this.user_id === Meteor.userId();\n    }\n\n(Of course, to make the above work, you would have to save your documents with a `user_id` field that has a value equal to Meteor.userId() of the creator.)\n\n`userCanEdit` is really only useful for preventing text from being editable on the client in certain circumstances (by setting it to `false`). In the end, the only logic that matters is that of the `EditableText.userCanEdit` function (see the 'Security' section below).\n\n`placeholder=\"New post\"` will be the placeholder on `input` or `textarea` elements\n\n`saveOnFocusout=false` will prevent a particular widget instance from saving the text being edited on a `focusout` event (the default is to save the text, which can be changed via `EditableText.saveOnFocusout`)\n\n`trustHtml=true` will make a particular widget instance rendered its text as HTML (default is `false`, which can be changed via `EditableText.trustHTML`)\n\n`stopPropagation=true` will stop the event that triggers the widget from propagating up the the elements ancestors on the DOM (default is `stopPropagation=false`, so the event will propagate)\n\nAll of these options can be set by using the `options=optionsHelper` parameter, where `optionsHelper` is a template helper that returns an object such as this:\n\n```\nTemplate.myTemplate.helpers({\n  optionsHelper : function() {\n    return {\n      collection: \"posts\",\n      field: \"title\",\n      removeEmpty: true,\n      acceptEmpty: true,\n      placeholder: \"Post title\",\n      substitute: '\u003ci class=\"fa fa-pencil\"\u003e\u003c/i\u003e'\n    }\n  }\n});\n```\n\n#### Triggering widget\n\nIf you wrap the `{{\u003e editableText ... }}` widget in an element which has `class=\"editable-text-trigger\"`, a click on that element will trigger the edititing.\n\n**Note:** Only use `class=\"editable-text-trigger\"` to trigger the widget with `eventType` values that are `click` (default), `dblclick` or `mousedown`. If you use other `eventType` values, you will run into problems with recursion.\n\n#### Transactions\n\nThere is built-in support for the `babrahams:transactions` package, if you want everything to be undo/redo-able. To enable this:\n\n    meteor add babrahams:transactions\n\nand in your app (in some config file on both client and server), add:\n\n    EditableText.useTransactions = true;\n\nOr if you only want transactions on particular instances of the widget, pass `useTransaction=true` or `useTransaction=false` to override the default that was set via `EditableText.useTransactions`, but this will only work if you also set `EditableText.clientControlsTransactions=true` (by default it is `false`). If you set the `EditableText.useTransactions` value on the server, without changing `EditableText.clientControlsTransactions`, it doesn't matter what you set on the client (or pass from the client), you will always get the behaviour as set on the server.\n\n#### Security\n\nTo control whether certain users can edit text on certain documents/fields, you can overwrite the function `EditableText.userCanEdit` (which gets the data and config passed to the widget as `this` and parameters which are the document and collection).  e.g. (to only allow users to edit their own documents):\n\n    EditableText.userCanEdit = function(doc, Collection) {\n      return this.context.user_id === Meteor.userId(); // same as: doc.user_id === Meteor.userId();\n    }\n\n**It is important that you overwrite this function in a production app** as the default is:\n\n    EditableText.userCanEdit = function(doc, Collection) {\n      return true;\n    }\n\n... which means anyone can edit any field in any document.\n\nIn this case, it is a good idea to make the `EditableText.userCanEdit` function and your allow and deny functions share the same logic to the greatest degree possible.\n\nNote: the default setting is `EditableText.useMethods=true`, meaning updates are processed server side and bypass your allow and deny rules. If you're happy with this (and you should be), then all you need to do for consistency between client and server permission checks is overwrite the `EditableText.userCanEdit` function in a file that is shared by both client and server.  Note that this function receives the widget data context as `this` and the collection object as the only parameter.\n\n    // e.g. If `type` is the editable field, but you want to limit the number of objects in the collection with any given value of `type` to 10\n    EditableText.userCanEdit = function(doc, Collection) {\n      var count = Collection.find({type: this.context.type}).count(); // `this.context` is a document from `Collection`\n      return count \u003c 10;\n    }\n\n**Warning:** if you set `EditableText.useMethods=false`, your data updates are being done on the client and you don't get html sanitization by default -- you'll have to sort this out or yourself via collection hooks or something. By default (i.e. when `EditableText.useMethods=true`) all data going into the database is passed through [htmlSantizer](https://github.com/punkave/sanitize-html).\n\n**Bigger warning:** it doesn't really matter what you set `EditableText.useMethods` to -- you still need to lock down your collections using appropriate `allow` and `deny` rules. A malicious user can just type `EditableText.useMethods=false` into the browser console and this package will start making client side changes whose persistence to the database are subject only to your `allow` and `deny` rules.\n\n#### Roadmap\n\n- ~~Factor out the wysiwyg editor and let it be added optionally via another package~~\n\n- ~~Make updates via methods rather than on the client using allow/deny rules~~\n\n- ~~Sanitize all html that comes through method calls (assume every string field is html)~~\n\n- ~~Add support for fields like `author.firstName`~~\n\n- Clean up and document code base\n\n- Put in error messages to help developers use the widget successfully\n\n- Write some tests\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackadams%2Fmeteor-editable-text","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjackadams%2Fmeteor-editable-text","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackadams%2Fmeteor-editable-text/lists"}