');
63 |
64 | function formElement(property) {
65 | property = property.replace('$${', '').replace('}', '');
66 | return '
';
67 | }
68 |
69 | //make our snippet look nice in the editor
70 | htmlOutput = htmlOutput.replace(//g, '>')
72 | .replace(/(\r\n|\n|\r)/gm, '
')
73 | .replace(/\t/g, ' ')
74 | .replace('!!{cursor}', '')
75 | .replace(/ /g, ' ');
76 |
77 | //turn the snippets into form fields
78 | for (x = 0; x < this.props.length; x++) {
79 | htmlOutput = htmlOutput.split(this.props[x]).join(formElement(this.props[x]));
80 | }
81 |
82 | this.$form.append(htmlOutput);
83 |
84 | //size the inputs to the placeholders and changing text
85 | this.$form.find('input').each(function () {
86 | var $input = $(this);
87 | var newWidth = 0;
88 | $input.width(textWidth($(this).attr('placeholder')));
89 | $input.keyup(function () {
90 | if ($input.is(':focus')) {
91 | if ($(this).val() === "") {
92 | newWidth = textWidth($(this).attr('placeholder'));
93 | } else {
94 | //dash gives it some extra space while typing
95 | newWidth = textWidth($(this).val());
96 | }
97 | $input.parent().find('.snipvar-' + $(this).attr('data-snippet')).width(newWidth);
98 | $input.parent().find('.snipvar-' + $(this).attr('data-snippet')).not(this).val($(this).val());
99 | }
100 | });
101 | });
102 |
103 | //listen for keypress
104 | this.$form.keydown(function (e) {
105 | //on enter, complete snippet
106 | if (e.which === 13) {
107 | e.stopImmediatePropagation();
108 | e.preventDefault();
109 | $(this).trigger('complete');
110 | } else if (e.which === 9) {
111 | //we will control the tabing
112 | e.stopImmediatePropagation();
113 | e.preventDefault();
114 |
115 | //select the next empty element unless there is no next element... or the next element is the current element
116 | var next = $(this).find('input').filter(function () { return $(this).val() === ""; });
117 | if (next.length && !$(next[0]).is(':focus')) {
118 | $(next[0]).focus();
119 | } else {
120 | $(this).trigger('complete');
121 | }
122 | }
123 | });
124 |
125 | this.$htmlContent.addClass('snippet-form-widget');
126 | this.$htmlContent.append(this.$form);
127 | };
128 |
129 | InlineSnippetForm.prototype.close = function () {
130 | this.hostEditor.removeInlineWidget(this);
131 | };
132 | InlineSnippetForm.prototype.onAdded = function () {
133 | InlineSnippetForm.prototype.parentClass.onAdded.apply(this, arguments);
134 | window.setTimeout(this._sizeEditorToContent.bind(this));
135 | this.$form.find('input').first().focus();
136 | };
137 |
138 | InlineSnippetForm.prototype._sizeEditorToContent = function () {
139 | this.hostEditor.setInlineWidgetHeight(this, this.$form.height(), true);
140 | };
141 |
142 | module.exports = InlineSnippetForm;
143 | });
144 |
--------------------------------------------------------------------------------
/src/Preferences.js:
--------------------------------------------------------------------------------
1 | /*global brackets, define */
2 |
3 | define(function (require, exports, module) {
4 | "use strict";
5 |
6 | var PREFERENCES_KEY = "brackets-snippets",
7 | _ = brackets.getModule("thirdparty/lodash"),
8 | PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
9 | prefs = PreferencesManager.getExtensionPrefs(PREFERENCES_KEY);
10 |
11 | var defaultPreferences = {
12 | "snippetsDirectory": { "type": "string", "value": "data" },
13 | "triggerSnippetShortcut": { "type": "string", "value": "Ctrl-Alt-Space" },
14 | "showSnippetsPanelShortcut": { "type": "string", "value": "" }
15 | };
16 |
17 | _.each(defaultPreferences, function (definition, key) {
18 | if (definition.os && definition.os[brackets.platform]) {
19 | prefs.definePreference(key, definition.type, definition.os[brackets.platform].value);
20 | } else {
21 | prefs.definePreference(key, definition.type, definition.value);
22 | }
23 | });
24 | prefs.save();
25 |
26 | prefs.getAll = function () {
27 | var obj = {};
28 | _.each(defaultPreferences, function (definition, key) {
29 | obj[key] = this.get(key);
30 | }, this);
31 | return obj;
32 | };
33 |
34 | prefs.getDefaults = function () {
35 | var obj = {};
36 | _.each(defaultPreferences, function (definition, key) {
37 | var defaultValue;
38 | if (definition.os && definition.os[brackets.platform]) {
39 | defaultValue = definition.os[brackets.platform].value;
40 | } else {
41 | defaultValue = definition.value;
42 | }
43 | obj[key] = defaultValue;
44 | }, this);
45 | return obj;
46 | };
47 |
48 | prefs.persist = function (key, value) {
49 | this.set(key, value);
50 | this.save();
51 | };
52 |
53 | module.exports = prefs;
54 | });
55 |
--------------------------------------------------------------------------------
/src/SettingsDialog.js:
--------------------------------------------------------------------------------
1 | /*global $, brackets, define, Mustache */
2 |
3 | define(function (require, exports) {
4 | "use strict";
5 |
6 | var CommandManager = brackets.getModule("command/CommandManager"),
7 | Dialogs = brackets.getModule("widgets/Dialogs"),
8 | Preferences = require("./Preferences"),
9 | Strings = require("../strings"),
10 | settingsDialogTemplate = require("text!templates/settings-dialog.html"),
11 | questionDialogTemplate = require("text!templates/question-dialog.html");
12 |
13 | var dialog,
14 | $dialog;
15 |
16 | function setValues(values) {
17 | $("*[settingsProperty]", $dialog).each(function () {
18 | var $this = $(this),
19 | type = $this.attr("type"),
20 | property = $this.attr("settingsProperty");
21 | if (type === "checkbox") {
22 | $this.prop("checked", values[property]);
23 | } else {
24 | $this.val(values[property]);
25 | }
26 | });
27 | }
28 |
29 | function collectValues() {
30 | $("*[settingsProperty]", $dialog).each(function () {
31 | var $this = $(this),
32 | type = $this.attr("type"),
33 | property = $this.attr("settingsProperty");
34 | if (type === "checkbox") {
35 | Preferences.set(property, $this.prop("checked"));
36 | } else {
37 | Preferences.set(property, $this.val().trim() || null);
38 | }
39 | });
40 | Preferences.save();
41 | }
42 |
43 | function assignActions() {
44 | $("button[data-button-id='defaults']", $dialog).on("click", function (e) {
45 | e.stopPropagation();
46 | setValues(Preferences.getDefaults());
47 | });
48 | }
49 |
50 | function init() {
51 | setValues(Preferences.getAll());
52 | assignActions();
53 | }
54 |
55 | function showRestartDialog() {
56 | var compiledTemplate = Mustache.render(questionDialogTemplate, {
57 | title: Strings.RESTART,
58 | question: Strings.Q_RESTART_BRACKETS,
59 | Strings: Strings
60 | });
61 | Dialogs.showModalDialogUsingTemplate(compiledTemplate).done(function (buttonId) {
62 | if (buttonId === "ok") {
63 | CommandManager.execute("debug.refreshWindow");
64 | }
65 | });
66 | }
67 |
68 | exports.show = function () {
69 | var compiledTemplate = Mustache.render(settingsDialogTemplate, Strings);
70 |
71 | dialog = Dialogs.showModalDialogUsingTemplate(compiledTemplate);
72 | $dialog = dialog.getElement();
73 |
74 | init();
75 |
76 | dialog.done(function (buttonId) {
77 | if (buttonId === "ok") {
78 | // Save everything to preferences
79 | collectValues();
80 | // Restart brackets to reload changes.
81 | showRestartDialog();
82 | }
83 | });
84 | };
85 | });
86 |
--------------------------------------------------------------------------------
/src/SnippetPresets.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Jonathan Rowny
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | *
22 | */
23 |
24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
25 | /*global define, brackets */
26 |
27 | define(function (require, exports) {
28 | 'use strict';
29 | var DocumentManager = brackets.getModule("document/DocumentManager");
30 |
31 | require("./date-format");
32 |
33 | var now,
34 | doc,
35 | presets = {
36 | //$${DATE} Ex: 11/17/2007
37 | "$${DATE}": function () {
38 | return now.format("shortDate");
39 | },
40 | //$${MONTH} Ex: November
41 | "$${MONTH}" : function () {
42 | return now.format("mmmm");
43 | },
44 | ///$${TIME} Ex: 14:25:00 PM
45 | "$${TIME}" : function () {
46 | return now.format("H:MM:ss TT");
47 | },
48 | //$${DATETIME} Ex: 11/17/2007 2:42:00 PM
49 | "$${DATETIME}" : function () {
50 | return now.format("m/d/yyyy h:MM:ss TT");
51 | },
52 | //$${DAYOFWEEK} Ex: Friday
53 | "$${DAYOFWEEK}" : function () {
54 | return now.format("dddd");
55 | },
56 | //$${CURRENTFILE} Ex: index.html - Current file name
57 | "$${CURRENTFILE}" : function () {
58 | return doc.file.name;
59 | },
60 | //$${CURRENTFOLDER} Ex: D:\workspace\myproject - Current folder
61 | "$${CURRENTFOLDER}" : function () {
62 | return doc.file.fullPath.replace("/" + doc.file.name, "");
63 | },
64 | //$${CURRENTPATH} Ex: D:\workspace\myproject\index.html - Full path
65 | "$${CURRENTPATH}" : function () {
66 | return doc.file.fullPath;
67 | },
68 | //$${CURRENTPRJPATH} Ex: myproject - Just the folder
69 | "$${CURRENTPRJPATH}" : function () {
70 | //TODO: this probably needs testing on mac
71 | var dirs = doc.file.fullPath.split("/");
72 | return dirs[dirs.length - 2];
73 | },
74 | //$${USERNAME} Current user NOT IMPLEMENTED
75 | /*"$${USERNAME}" : function () {
76 |
77 | },*/
78 | //$${MONTHNUMBER} Month as a number
79 | "$${MONTHNUMBER}" : function () {
80 | return now.format("m");
81 | },
82 | //$${DAYOFMONTH} Day of month as a number
83 | "$${DAYOFMONTH}" : function () {
84 | return now.format("d");
85 | },
86 | //$${DAYOFWEEKNUMBER} Day of week (the week starts on Sunday)
87 | "$${DAYOFWEEKNUMBER}" : function () {
88 | return now.getDay() + 1;
89 | },
90 | //$${DATETIME24} Ex: 01/12/2007 14:42:00 - 24 hour clock version of datetime.
91 | "$${DATETIME24}" : function () {
92 | return now.format("m/d/yyyy H:MM:ss TT");
93 | },
94 | //$${YEAR} Ex: 2007 - Current year
95 | "$${YEAR}" : function () {
96 | return now.getFullYear();
97 | },
98 | //$${YEAR2DIGIT} Ex: 07 - Current two digit year
99 | "$${YEAR2DIGIT}" : function () {
100 | return now.getFullYear().toString().slice("1");
101 | },
102 | //$${CURRENTFILENOEXT} The name of the current file with no extension Ex: "index" for "index.html"
103 | "$${CURRENTFILENOEXT}" : function () {
104 | return doc.file.name.replace("." + doc.extension, "");
105 | }
106 | };
107 |
108 | function _execute(input) {
109 | doc = DocumentManager.getCurrentDocument();
110 | now = new Date();
111 | var key;
112 | for (key in presets) {
113 | if (presets.hasOwnProperty(key) && input.indexOf(key) > -1) {
114 | input = input.replace(key, presets[key].call());
115 | }
116 | }
117 | return input;
118 | }
119 | exports.execute = _execute;
120 |
121 | });
122 |
--------------------------------------------------------------------------------
/src/date-format.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Date Format 1.2.3
3 | * (c) 2007-2009 Steven Levithan
4 | * MIT license
5 | *
6 | * Includes enhancements by Scott Trenda
7 | * and Kris Kowal
8 | *
9 | * Accepts a date, a mask, or a date and a mask.
10 | * Returns a formatted version of the given date.
11 | * The date defaults to the current date/time.
12 | * The mask defaults to dateFormat.masks.default.
13 | */
14 |
15 | var dateFormat = function () {
16 | var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
17 | timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
18 | timezoneClip = /[^-+\dA-Z]/g,
19 | pad = function (val, len) {
20 | val = String(val);
21 | len = len || 2;
22 | while (val.length < len) val = "0" + val;
23 | return val;
24 | };
25 |
26 | // Regexes and supporting functions are cached through closure
27 | return function (date, mask, utc) {
28 | var dF = dateFormat;
29 |
30 | // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
31 | if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
32 | mask = date;
33 | date = undefined;
34 | }
35 |
36 | // Passing date through Date applies Date.parse, if necessary
37 | date = date ? new Date(date) : new Date;
38 | if (isNaN(date)) throw SyntaxError("invalid date");
39 |
40 | mask = String(dF.masks[mask] || mask || dF.masks["default"]);
41 |
42 | // Allow setting the utc argument via the mask
43 | if (mask.slice(0, 4) == "UTC:") {
44 | mask = mask.slice(4);
45 | utc = true;
46 | }
47 |
48 | var _ = utc ? "getUTC" : "get",
49 | d = date[_ + "Date"](),
50 | D = date[_ + "Day"](),
51 | m = date[_ + "Month"](),
52 | y = date[_ + "FullYear"](),
53 | H = date[_ + "Hours"](),
54 | M = date[_ + "Minutes"](),
55 | s = date[_ + "Seconds"](),
56 | L = date[_ + "Milliseconds"](),
57 | o = utc ? 0 : date.getTimezoneOffset(),
58 | flags = {
59 | d: d,
60 | dd: pad(d),
61 | ddd: dF.i18n.dayNames[D],
62 | dddd: dF.i18n.dayNames[D + 7],
63 | m: m + 1,
64 | mm: pad(m + 1),
65 | mmm: dF.i18n.monthNames[m],
66 | mmmm: dF.i18n.monthNames[m + 12],
67 | yy: String(y).slice(2),
68 | yyyy: y,
69 | h: H % 12 || 12,
70 | hh: pad(H % 12 || 12),
71 | H: H,
72 | HH: pad(H),
73 | M: M,
74 | MM: pad(M),
75 | s: s,
76 | ss: pad(s),
77 | l: pad(L, 3),
78 | L: pad(L > 99 ? Math.round(L / 10) : L),
79 | t: H < 12 ? "a" : "p",
80 | tt: H < 12 ? "am" : "pm",
81 | T: H < 12 ? "A" : "P",
82 | TT: H < 12 ? "AM" : "PM",
83 | Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
84 | o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
85 | S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
86 | };
87 |
88 | return mask.replace(token, function ($0) {
89 | return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
90 | });
91 | };
92 | }();
93 |
94 | // Some common format strings
95 | dateFormat.masks = {
96 | "default": "ddd mmm dd yyyy HH:MM:ss",
97 | shortDate: "m/d/yy",
98 | mediumDate: "mmm d, yyyy",
99 | longDate: "mmmm d, yyyy",
100 | fullDate: "dddd, mmmm d, yyyy",
101 | shortTime: "h:MM TT",
102 | mediumTime: "h:MM:ss TT",
103 | longTime: "h:MM:ss TT Z",
104 | isoDate: "yyyy-mm-dd",
105 | isoTime: "HH:MM:ss",
106 | isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
107 | isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
108 | };
109 |
110 | // Internationalization strings
111 | dateFormat.i18n = {
112 | dayNames: [
113 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
114 | "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
115 | ],
116 | monthNames: [
117 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
118 | "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
119 | ]
120 | };
121 |
122 | // For convenience...
123 | Date.prototype.format = function (mask, utc) {
124 | return dateFormat(this, mask, utc);
125 | };
--------------------------------------------------------------------------------
/strings.js:
--------------------------------------------------------------------------------
1 | /*global define */
2 |
3 | /**
4 | * This file provides the interface to user visible strings in Brackets. Code that needs
5 | * to display strings should should load this module by calling var Strings = require("strings").
6 | * The i18n plugin will dynamically load the strings for the right locale and populate
7 | * the exports variable. See nls/root/strings.js for the master file of English strings.
8 | */
9 | define(function (require, exports, module) {
10 | "use strict";
11 | module.exports = require("i18n!nls/strings");
12 | });
13 |
--------------------------------------------------------------------------------
/styles/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jrowny/brackets-snippets/9fde264ee6cca0140d507883be5ccd66f4ec68d4/styles/images/icon.png
--------------------------------------------------------------------------------
/styles/snippets.css:
--------------------------------------------------------------------------------
1 | /* All selectors are descendents of #snippets to constrain styles to this extension */
2 |
3 | /*INLINE FORM*/
4 | .snippet-form-widget .shadow{
5 | display:none;
6 | }
7 | .snippet-form-widget{
8 | background:transparent;
9 | border-top:1px dashed #e5e5e5;
10 | border-bottom:1px dashed #e5e5e5;
11 | padding:5px 0 5px 80px ;
12 | }
13 |
14 | .snippet-form input{
15 | border:0;
16 | padding:0 3px;
17 | margin:0;
18 | border-radius:0;
19 | background: transparent;
20 | box-shadow:none;
21 | border-bottom:1px dashed #4d62ff;
22 | }
23 |
24 | /*PANEL*/
25 | #snippets .resizable-content {
26 | height: 175px;
27 | overflow: auto;
28 | }
29 |
30 | #snippets table td {
31 | line-height: 14px;
32 | padding: 4px 10px;
33 | }
34 |
35 | #snippets table td a {
36 | cursor: pointer;
37 | }
38 |
39 | #snippets table .snippets-header td {
40 | font-weight: bold;
41 | background-color: #404040;
42 | padding: 0;
43 | color: #fff;
44 | padding: 4px 10px;
45 | }
46 |
47 | #snippets .toolbar {
48 | display: block;
49 | }
50 |
51 | #snippets .toolbar .snippets-close {
52 | font-weight: bold;
53 | font-height: 20px;
54 | color: #999;
55 | margin-right: 5px;
56 | }
57 |
58 | #snippets .toolbar .snippets-settings {
59 | margin-right: 50px;
60 | }
61 |
62 | #snippets .toolbar .snippets-close:hover {
63 | text-decoration: none;
64 | color: #000;
65 | }
66 |
67 | #snippets .toolbar button.copy-table {
68 | border: 1px solid #ccc;
69 | border-radius: 3px;
70 | float: right;
71 | padding: 0 10px;
72 | margin-right: 20px;
73 | font-size: 90%;
74 | }
75 |
76 | #snippets-enable-icon {
77 | background-image: url(images/icon.png);
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/templates/bottom-panel.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/templates/question-dialog.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | {{question}}
7 | {{#stringInput}}
8 |
9 | {{/stringInput}}
10 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/templates/settings-dialog.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{SHORTCUTS_HINT}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
35 |
36 |
--------------------------------------------------------------------------------
/templates/snippets-table.html:
--------------------------------------------------------------------------------
1 |
2 |
9 | {{#snippets}}
10 |
11 | {{name}} |
12 | {{description}} |
13 | {{trigger}} |
14 | {{usage}} |
15 | {{source}} |
16 |
17 | {{/snippets}}
18 |
--------------------------------------------------------------------------------