├── .gitignore ├── .jshintrc ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── data ├── html.json ├── javascript.json └── snippets │ └── html5.snippet ├── main.js ├── nls ├── de │ └── strings.js ├── es │ └── strings.js ├── gl │ └── strings.js ├── root │ └── strings.js └── strings.js ├── package.json ├── src ├── InlineSnippetForm.js ├── Preferences.js ├── SettingsDialog.js ├── SnippetPresets.js └── date-format.js ├── strings.js ├── styles ├── images │ └── icon.png └── snippets.css └── templates ├── bottom-panel.html ├── question-dialog.html ├── settings-dialog.html └── snippets-table.html /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "immed": true, 6 | "indent": 4, 7 | "latedef": true, 8 | "newcap": true, 9 | "noarg": true, 10 | "noempty": true, 11 | "nonew": true, 12 | "quotmark": false, 13 | "undef": true, 14 | "unused": true, 15 | "strict": false, 16 | "trailing": true, 17 | "maxparams": 5, 18 | "maxdepth": 5, 19 | "maxstatements": 50, 20 | "maxlen": 200, 21 | 22 | "eqnull": true, 23 | "esnext": false, 24 | "loopfunc": false, 25 | 26 | "browser": true, 27 | "devel": true, 28 | "jquery": true, 29 | "node": true, 30 | "nonstandard": true, 31 | "white": true, 32 | 33 | "globals": { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.1.0 4 | * Added new settings panel - [#34](https://github.com/jrowny/brackets-snippets/pull/34) 5 | * Added support for inline snippets - [#15](https://github.com/jrowny/brackets-snippets/issues/15) 6 | * Added support for string parameters - [#10](https://github.com/jrowny/brackets-snippets/issues/10) 7 | * Default snippet shortcut changed to Ctrl-Alt-Space - [#19](https://github.com/jrowny/brackets-snippets/issues/19) 8 | * Snippets now work on the first line - [#28](https://github.com/jrowny/brackets-snippets/issues/28) 9 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | "use strict"; 3 | 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON("package.json"), 6 | jshint: { 7 | files: ["*.js", "src/**/*.js", "nls/**/*.js"], 8 | options: { 9 | jshintrc: true 10 | } 11 | } 12 | }); 13 | 14 | grunt.loadNpmTasks("grunt-contrib-jshint"); 15 | grunt.registerTask("default", ["jshint"]); 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jonathan Rowny and other contributors 4 | https://github.com/jrowny/brackets-snippets/graphs/contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | brackets-snippets 2 | ================= 3 | 4 | Brackets Snippets extension. 5 | 6 | Extension is available in the [Brackets extension registry](https://brackets-registry.aboutweb.com/). 7 | 8 | To install, go to the Brackets Extension Manager and search for `brackets snippets`. 9 | 10 | Usage 11 | ===== 12 | Icon on the right toolbar shows you what snippets are available. Click any snippet to begin inserting.
13 | **Ctrl-Alt-Space** to insert the snippet. (Cmd-Alt-Space on Mac). 14 | 15 | type a trigger followed by parameters (optional), then hit Ctrl-Alt-V 16 | 17 | ```f myFunc``` becomes 18 | 19 | ``` 20 | function myFunc () { 21 | } 22 | ``` 23 | 24 | ```for x myArray``` 25 | becomes: 26 | ``` 27 | var x; 28 | for (x = 0; x < myArray.length; x++) { 29 | 30 | } 31 | ``` 32 | 33 | If you omit parameters, an inline form will appear. Use `ESC` to close the inline form or `ENTER` to complete the insertion. 34 | 35 | Adding Snippets 36 | =============== 37 | You can create new JSON files in the ```data``` directory or you can edit the existing ```javascript.json``` file. Your JSON files can reference template files if they have a `.snippet` extension and are in the `data\snippets` directory. See html5.snippet for an example. 38 | -------------------------------------------------------------------------------- /data/html.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "html5", 4 | "trigger": "html5", 5 | "usage": "html5", 6 | "description": "Create an HTML5 page", 7 | "template": "html5.snippet" 8 | }, 9 | { 10 | "name": "link", 11 | "trigger": "a", 12 | "usage": "a index.html home", 13 | "description": "Create an achor with an href", 14 | "template": "$${title}" 15 | } 16 | ] -------------------------------------------------------------------------------- /data/javascript.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Function", 4 | "trigger": "f", 5 | "usage": "f myFunc", 6 | "description": "Create a javascript function", 7 | "template": "function $${name}() {\n!!{cursor}\n}" 8 | }, 9 | { 10 | "name": "For Loop", 11 | "trigger": "for", 12 | "usage": "for x someArray", 13 | "description": "Create a javascript for loop", 14 | "template": "var $${iterator};\nvar $${limit} = $${container}.length;\nfor ($${iterator} = 0; $${iterator} < $${limit}; $${iterator}++) {\n!!{cursor}\n}" 15 | }, 16 | { 17 | "name": "For Each", 18 | "trigger": "foreach", 19 | "usage": "foreach itemList item", 20 | "description": "Create a javascript foreach loop", 21 | "template": "$${container}.forEach(function ($${subject}) {\n!!{cursor}\n});" 22 | }, 23 | { 24 | "name": "Requires", 25 | "trigger": "req", 26 | "usage": "req reference module", 27 | "description": "Create a javascript requires block", 28 | "template": "var $${name} = require(\"$${path}\");" 29 | }, 30 | { 31 | "name": "Immediately-Invoked Function", 32 | "trigger": "iife", 33 | "usage": "iife", 34 | "description": "Create a javascript immediately-invoked function", 35 | "template": "(function () {\n\"use strict\";\n\n!!{cursor}\n}());" 36 | }, 37 | { 38 | "name": "Sample inline script for console logging", 39 | "trigger": "log", 40 | "usage": "log x", 41 | "description": "Log a message into console", 42 | "template": "console.log($${message});!!{cursor}", 43 | "inline": true 44 | } 45 | ] 46 | -------------------------------------------------------------------------------- /data/snippets/html5.snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | !!{cursor} 12 | 13 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Jonathan Rowny. All rights reserved. 3 | * http://www.jonathanrowny.com 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | * DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 25 | /*global define, brackets, $, Mustache */ 26 | 27 | define(function (require, exports, module) { 28 | 29 | var _ = brackets.getModule("thirdparty/lodash"), 30 | AppInit = brackets.getModule("utils/AppInit"), 31 | Commands = brackets.getModule("command/Commands"), 32 | CommandManager = brackets.getModule("command/CommandManager"), 33 | EditorManager = brackets.getModule("editor/EditorManager"), 34 | DocumentManager = brackets.getModule("document/DocumentManager"), 35 | ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), 36 | FileSystem = brackets.getModule("filesystem/FileSystem"), 37 | KeyBindingManager = brackets.getModule("command/KeyBindingManager"), 38 | FileUtils = brackets.getModule("file/FileUtils"), 39 | Menus = brackets.getModule("command/Menus"), 40 | PanelManager = brackets.getModule("view/PanelManager"); 41 | 42 | // Local modules 43 | var InlineSnippetForm = require("src/InlineSnippetForm"), 44 | Preferences = require("src/Preferences"), 45 | SettingsDialog = require("src/SettingsDialog"), 46 | SnippetPresets = require("src/SnippetPresets"), 47 | panelHtml = require("text!templates/bottom-panel.html"), 48 | snippetsHTML = require("text!templates/snippets-table.html"); 49 | 50 | //Snippets array 51 | var snippets = [], 52 | //directory where snippets are 53 | snippetsDirectory = Preferences.get("snippetsDirectory").replace(/\\/g, "/"), 54 | $snippetsPanel, 55 | $snippetsContent, 56 | panel; 57 | 58 | //commands 59 | var SNIPPET_EXECUTE = "snippets.execute", 60 | VIEW_HIDE_SNIPPETS = "snippets.hideSnippets"; 61 | 62 | function _handleHideSnippets() { 63 | if (panel.isVisible()) { 64 | panel.hide(); 65 | CommandManager.get(VIEW_HIDE_SNIPPETS).setChecked(false); 66 | } else { 67 | panel.show(); 68 | CommandManager.get(VIEW_HIDE_SNIPPETS).setChecked(true); 69 | } 70 | EditorManager.resizeEditor(); 71 | } 72 | 73 | function inlineSnippetFormProvider(hostEditor, props, snippet) { 74 | var result = new $.Deferred(); 75 | 76 | var snippetForm = new InlineSnippetForm(props, snippet); 77 | snippetForm.load(hostEditor); 78 | result.resolve(snippetForm); 79 | 80 | return result.promise(); 81 | } 82 | 83 | function _parseArgs(str) { 84 | str = str.trim(); 85 | 86 | var result = [], 87 | current = "", 88 | inQuotes = false, 89 | quotes = ['"', "'"]; 90 | 91 | for (var i = 0, l = str.length; i < l; i++) { 92 | if (str[i] === " " && inQuotes === false) { 93 | if (current.length > 0) { 94 | result.push(current); 95 | current = ""; 96 | } 97 | continue; 98 | } 99 | if (inQuotes && str[i] === inQuotes) { 100 | inQuotes = false; 101 | } 102 | if (current.length === 0 && quotes.indexOf(str[i]) !== -1) { 103 | inQuotes = str[i]; 104 | } 105 | current += str[i]; 106 | } 107 | result.push(current); 108 | 109 | return result; 110 | } 111 | 112 | function _handleSnippet(props) { 113 | var editor = EditorManager.getCurrentFullEditor(), 114 | document = DocumentManager.getCurrentDocument(), 115 | pos = editor.getCursorPos(), 116 | line = document.getLine(pos.line), 117 | preInline = null, 118 | postInline = null; 119 | 120 | if (!props) { 121 | props = _parseArgs(line); 122 | } 123 | 124 | function completeInsert(editor, pos, output) { 125 | var s, 126 | x, 127 | cursorPos, 128 | cursorOffset; 129 | 130 | // add back text that was found before inline snippet 131 | if (preInline) { 132 | output = preInline.join(" ") + " " + output; 133 | } 134 | if (postInline) { 135 | output = output + " " + postInline.join(" "); 136 | } 137 | 138 | var lines = output.split("\n"); 139 | 140 | //figure out cursor pos, remove cursor marker 141 | for (s = 0; s < lines.length; s++) { 142 | cursorOffset = lines[s].indexOf('!!{cursor}'); 143 | if (cursorOffset >= 0) { 144 | cursorPos = s; 145 | output = output.replace('!!{cursor}', ''); 146 | break; 147 | } 148 | } 149 | 150 | //do insertion 151 | document.replaceRange(output + "\n", {line: pos.line, ch: 0}, {line: pos.line, ch: 0}); 152 | 153 | //set cursor 154 | editor._codeMirror.setCursor(pos.line + cursorPos, cursorOffset); 155 | 156 | //indent lines 157 | for (x = 0; x < lines.length; x++) { 158 | editor._codeMirror.indentLine(pos.line + x); 159 | } 160 | 161 | //give focus back 162 | EditorManager.focusEditor(); 163 | } 164 | function startInsert(output) { 165 | //we don't need to see the trigger text 166 | CommandManager.execute(Commands.EDIT_DELETE_LINES); 167 | 168 | //find variables 169 | var tmp = output.match(/\$\$\{[0-9A-Z_a-z]{1,32}\}/g); 170 | //remove duplicate variables 171 | var snippetVariables = [], 172 | j; 173 | 174 | if (tmp && tmp.length > 0) { 175 | for (j = 0; j < tmp.length; j++) { 176 | if ($.inArray(tmp[j], snippetVariables) === -1) { 177 | snippetVariables.push(tmp[j]); 178 | } 179 | } 180 | } 181 | 182 | var variablesDifference = props.length - 1 - snippetVariables.length; 183 | 184 | if (variablesDifference > 0) { 185 | // we have more variables than we require 186 | var mid = snippetVariables.length + 1; 187 | postInline = props.slice(mid); 188 | props = props.slice(0, mid); 189 | variablesDifference = 0; 190 | } 191 | 192 | if (variablesDifference === 0) { 193 | // we have exactly as many variables as we need 194 | for (var x = 0; x < snippetVariables.length; x++) { 195 | //even my escapes have escapes 196 | var re = new RegExp(snippetVariables[x].replace('$${', '\\$\\$\\{').replace('}', '\\}'), 'g'); 197 | output = output.replace(re, props[x + 1]); 198 | } 199 | completeInsert(editor, pos, output); 200 | } else { 201 | // we have less variables than we need 202 | var snippetPromise, 203 | result = new $.Deferred(); 204 | snippetPromise = inlineSnippetFormProvider(editor, snippetVariables, output); 205 | 206 | snippetPromise.done(function (inlineWidget) { 207 | var newPos = {line: pos.line - 1, ch: pos.ch}; 208 | editor.addInlineWidget(newPos, inlineWidget); 209 | var inlineComplete = function () { 210 | var z; 211 | for (z = 0; z < snippetVariables.length; z++) { 212 | //even my escapes have escapes 213 | var re = new RegExp(snippetVariables[z].replace('$${', '\\$\\$\\{').replace('}', '\\}'), 'g'); 214 | output = output.replace(re, inlineWidget.$form.find('.snipvar-' + snippetVariables[z].replace('$${', '').replace('}', '')).val()); 215 | } 216 | 217 | completeInsert(editor, pos, output); 218 | }; 219 | inlineWidget.$form.on('complete', function () { 220 | inlineComplete(); 221 | inlineWidget.close(); 222 | }); 223 | }).fail(function () { 224 | result.reject(); 225 | console.log("Can't create inline snippet form"); 226 | }); 227 | } 228 | } 229 | 230 | function readSnippetFromFile(fileName) { 231 | var snippetFilePath = snippetsDirectory + '/snippets/' + fileName; 232 | FileSystem.resolve(snippetFilePath, function (err, snippetFile) { 233 | if (err) { 234 | FileUtils.showFileOpenError(err, snippetFilePath); 235 | return; 236 | } 237 | 238 | snippetFile.read(function (err, text) { 239 | if (err) { 240 | FileUtils.showFileOpenError(err, snippetFile.fullPath); 241 | return; 242 | } 243 | 244 | startInsert(SnippetPresets.execute(text)); 245 | }); 246 | }); 247 | } 248 | 249 | if (props.length) { 250 | //try to find the snippet, given the trigger text 251 | var i, 252 | triggers = _.pluck(snippets, "trigger"); 253 | //go in backwards order for a case there is an inline snippet along the way 254 | for (i = props.length - 1; i > 0; i--) { 255 | 256 | var io, 257 | requireInline = true; 258 | 259 | // launch non-inline in inline mode snippets when %trigger is found 260 | if (props[i][0] === "%") { 261 | requireInline = false; 262 | io = triggers.indexOf(props[i].substring(1)); 263 | } else { 264 | io = triggers.indexOf(props[i]); 265 | } 266 | 267 | if (io !== -1 && (snippets[io].inline || !requireInline)) { 268 | // found inline snippet 269 | preInline = props.slice(0, i); 270 | props = props.slice(i); 271 | startInsert(SnippetPresets.execute(snippets[io].template)); 272 | return; 273 | } 274 | } 275 | //no inline snippet found so look for any snippet that matches props[0] 276 | //it can also have % as a prefix so remove 277 | if (props[0][0] === "%") { 278 | props[0] = props[0].substring(1); 279 | } 280 | var snippet = _.find(snippets, function (s) { 281 | return s.trigger === props[0]; 282 | }); 283 | if (snippet) { 284 | var output = snippet.template; 285 | if (output.indexOf('.snippet') === output.length - 8) { 286 | readSnippetFromFile(output); 287 | } else { 288 | startInsert(SnippetPresets.execute(output)); 289 | } 290 | } 291 | } 292 | } 293 | 294 | //builds the snippets table 295 | function setupSnippets() { 296 | var s = Mustache.render(panelHtml); 297 | panel = PanelManager.createBottomPanel(VIEW_HIDE_SNIPPETS, $(s), 100); 298 | panel.hide(); 299 | 300 | $snippetsPanel = $('#snippets'); 301 | $snippetsContent = $snippetsPanel.find(".resizable-content"); 302 | $snippetsPanel 303 | .on("click", ".snippets-settings", function () { 304 | SettingsDialog.show(); 305 | }) 306 | .on("click", ".snippets-close", function () { 307 | CommandManager.execute(VIEW_HIDE_SNIPPETS); 308 | }); 309 | } 310 | 311 | function finalizeSnippetsTable() { 312 | var i, 313 | len, 314 | $snippetsTable; 315 | for (i = 0, len = arguments.length; i < len; i++) { 316 | if (arguments[i] && arguments[i].length) { 317 | snippets = snippets.concat(arguments[i]); 318 | } 319 | } 320 | $snippetsTable = Mustache.render(snippetsHTML, {"snippets" : snippets}); 321 | $snippetsPanel.find('.resizable-content').append($snippetsTable); 322 | $snippetsPanel.find('.snippets-trigger').on('click', function () { 323 | CommandManager.execute(SNIPPET_EXECUTE, [$(this).attr('data-trigger')]); 324 | }); 325 | $snippetsPanel.find('.snippets-source').on('click', function () { 326 | CommandManager.execute(Commands.FILE_OPEN, { fullPath: snippetsDirectory + "/" + $(this).attr('data-source') }); 327 | }); 328 | } 329 | 330 | //parse a JSON file with a snippet in it 331 | function loadSnippet(fileEntry) { 332 | var result = new $.Deferred(); 333 | fileEntry.read(function (err, text) { 334 | if (err) { 335 | FileUtils.showFileOpenError(err, fileEntry.fullPath); 336 | result.reject(err); 337 | return; 338 | } 339 | 340 | try { 341 | //TODO: a better check for valid snippets 342 | var newSnippets = JSON.parse(text); 343 | newSnippets.forEach(function (item) { 344 | item.source = fileEntry.name; 345 | }); 346 | result.resolve(newSnippets); 347 | } catch (err) { 348 | console.error("Can't parse snippets from " + fileEntry.fullPath + " - " + err); 349 | result.reject(err); 350 | } 351 | }); 352 | return result; 353 | } 354 | 355 | CommandManager.register("Run Snippet", SNIPPET_EXECUTE, _handleSnippet); 356 | CommandManager.register("Show Snippets", VIEW_HIDE_SNIPPETS, _handleHideSnippets); 357 | 358 | function loadSnippets() { 359 | if (!FileSystem.isAbsolutePath(snippetsDirectory)) { 360 | snippetsDirectory = FileUtils.getNativeModuleDirectoryPath(module) + "/" + snippetsDirectory; 361 | } 362 | //loop through the directory to load snippets 363 | FileSystem.resolve(snippetsDirectory, function (err, rootEntry) { 364 | if (err) { 365 | console.error("[Snippets] Error -- could not open snippets directory: " + snippetsDirectory); 366 | console.error(err); 367 | return; 368 | } 369 | 370 | rootEntry.getContents(function (err, entries) { 371 | if (err) { 372 | console.error("[Snippets] Error -- could not read snippets directory: " + snippetsDirectory); 373 | console.error(err); 374 | return; 375 | } 376 | 377 | var loading = _.compact(entries.map(function (entry) { 378 | if (entry.name.charAt(0) === ".") { 379 | //ignore dotfiles 380 | return; 381 | } 382 | if (entry.isDirectory) { 383 | //ignore directories 384 | return; 385 | } 386 | return loadSnippet(entry); 387 | })); 388 | 389 | $.when.apply(module, loading).done(finalizeSnippetsTable); 390 | }); 391 | }); 392 | } 393 | 394 | AppInit.appReady(function () { 395 | //add the HTML UI 396 | setupSnippets(); 397 | 398 | //load css 399 | ExtensionUtils.loadStyleSheet(module, "styles/snippets.css"); 400 | 401 | //add the menu and keybinding for view/hide 402 | var menu = Menus.getMenu(Menus.AppMenuBar.VIEW_MENU); 403 | menu.addMenuItem(VIEW_HIDE_SNIPPETS, Preferences.get("showSnippetsPanelShortcut"), Menus.AFTER, Commands.VIEW_HIDE_SIDEBAR); 404 | 405 | // Add toolbar icon 406 | $("") 407 | .attr({ 408 | id: "snippets-enable-icon", 409 | href: "#" 410 | }) 411 | .click(_handleHideSnippets) 412 | .appendTo($("#main-toolbar .buttons")); 413 | 414 | //add the keybinding 415 | KeyBindingManager.addBinding(SNIPPET_EXECUTE, Preferences.get("triggerSnippetShortcut")); 416 | 417 | //load snippets 418 | loadSnippets(); 419 | }); 420 | 421 | }); 422 | -------------------------------------------------------------------------------- /nls/de/strings.js: -------------------------------------------------------------------------------- 1 | /*jshint maxlen:false */ 2 | /*global define */ 3 | 4 | define({ 5 | BUTTON_CANCEL: "Abbrechen", 6 | BUTTON_OK: "OK", 7 | BUTTON_RESTORE_DEFAULTS: "Standard wiederherstellen", 8 | BUTTON_SAVE: "Speichern", 9 | INSERT_SNIPPET: "Snippet einfügen", 10 | RESTART: "Neustart", 11 | Q_RESTART_BRACKETS: "Möchtest du Brackets neustarten um die Einstellungen anzuwenden?", 12 | SHORTCUTS: "Tastenkürzel", 13 | SHORTCUTS_HINT: "Tastenkürzel - Tasten mit Bindestrich trennen, z.B.: Ctrl-Alt-Space", 14 | SHOW_SNIPPET_PANEL: "Snippets Menü anzeigen", 15 | SNIPPETS_DIRECTORY: "Snippets Verzeichnis", 16 | SNIPPETS_SETTINGS: "Snippets Einstellungen" 17 | }); 18 | -------------------------------------------------------------------------------- /nls/es/strings.js: -------------------------------------------------------------------------------- 1 | /*jshint maxlen:false */ 2 | /*global define */ 3 | 4 | define({ 5 | BUTTON_CANCEL: "Cancelar", 6 | BUTTON_OK: "OK", 7 | BUTTON_RESTORE_DEFAULTS: "Restaurar", 8 | BUTTON_SAVE: "Guardar", 9 | INSERT_SNIPPET: "Intertar snippet", 10 | RESTART: "Reiniciar", 11 | Q_RESTART_BRACKETS: "¿Quieres reiniciar Brackets para aplicar los nuevos cambios?", 12 | SHORTCUTS: "Atajos", 13 | SHORTCUTS_HINT: "Atajos - separa las teclas con guiones: Ctrl-Alt-Space", 14 | SHOW_SNIPPET_PANEL: "Mostrar panel de snippets", 15 | SNIPPETS_DIRECTORY: "Directorio de los snippets", 16 | SNIPPETS_SETTINGS: "Ajustes de Snippets" 17 | }); 18 | -------------------------------------------------------------------------------- /nls/gl/strings.js: -------------------------------------------------------------------------------- 1 | /*jshint maxlen:false */ 2 | /*global define */ 3 | 4 | define({ 5 | BUTTON_CANCEL: "Cancelar", 6 | BUTTON_OK: "OK", 7 | BUTTON_RESTORE_DEFAULTS: "Restaurar", 8 | BUTTON_SAVE: "Gardar", 9 | INSERT_SNIPPET: "Intertar snippet", 10 | RESTART: "Reiniciar", 11 | Q_RESTART_BRACKETS: "¿Queres reiniciar Brackets para aplicar os novos cambios?", 12 | SHORTCUTS: "Atallos", 13 | SHORTCUTS_HINT: "Atallos - separa as teclas con guións: Ctrl-Alt-Space", 14 | SHOW_SNIPPET_PANEL: "Amosar panel de snippets", 15 | SNIPPETS_DIRECTORY: "Directorio dos snippets", 16 | SNIPPETS_SETTINGS: "Axustes de Snippets" 17 | }); 18 | -------------------------------------------------------------------------------- /nls/root/strings.js: -------------------------------------------------------------------------------- 1 | /*jshint maxlen:false */ 2 | /*global define */ 3 | 4 | define({ 5 | BUTTON_CANCEL: "Cancel", 6 | BUTTON_OK: "OK", 7 | BUTTON_RESTORE_DEFAULTS: "Restore defaults", 8 | BUTTON_SAVE: "Save", 9 | INSERT_SNIPPET: "Insert snippet", 10 | RESTART: "Restart", 11 | Q_RESTART_BRACKETS: "Do you wish to restart Brackets to apply new settings?", 12 | SHORTCUTS: "Shortcuts", 13 | SHORTCUTS_HINT: "Shortcuts - separate keys with dash like this: Ctrl-Alt-Space", 14 | SHOW_SNIPPET_PANEL: "Show snippets panel", 15 | SNIPPETS_DIRECTORY: "Snippets directory", 16 | SNIPPETS_SETTINGS: "Snippets Settings" 17 | }); 18 | -------------------------------------------------------------------------------- /nls/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @see: https://github.com/adobe/brackets/tree/master/src/extensions/samples/LocalizationExample 3 | */ 4 | 5 | /*global define */ 6 | 7 | define(function (require, exports, module) { 8 | "use strict"; 9 | 10 | // Code that needs to display user strings should call require("strings") to load 11 | // strings.js. This file will dynamically load strings.js for the specified by bracketes.locale. 12 | // 13 | // Translations for other locales should be placed in nls/>/strings.js 14 | // Localization is provided via the i18n plugin. 15 | // All other bundles for languages need to add a prefix to the exports below so i18n can find them. 16 | // 17 | // /TODO: dynamically populate the local prefix list below? 18 | module.exports = { 19 | root: true, 20 | "de": true, 21 | "es": true, 22 | "gl": true 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jrowny.brackets.snippets", 3 | "title": "Brackets Snippets", 4 | "description": "Provides ability to use code snippets in Brackets.", 5 | "homepage": "https://github.com/jrowny/brackets-snippets", 6 | "license": "MIT", 7 | 8 | "author": "Jonathan Rowny", 9 | "version": "1.1.0-dev", 10 | "engines": { "brackets": ">=0.36" }, 11 | 12 | "devDependencies": { 13 | "grunt": "latest", 14 | "grunt-contrib-jshint": "latest" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/InlineSnippetForm.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Jonathan Rowny. All rights reserved. 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 | 25 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 26 | /*global define, brackets, $, window */ 27 | 28 | define(function (require, exports, module) { 29 | 'use strict'; 30 | // Load Brackets modules 31 | var InlineWidget = brackets.getModule("editor/InlineWidget").InlineWidget; 32 | function InlineSnippetForm(props, snippet) { 33 | this.props = props; 34 | this.snippet = snippet; 35 | 36 | InlineWidget.call(this); 37 | } 38 | 39 | function textWidth(text) { 40 | var html = $('' + text + ''); 41 | $('body').append(html); 42 | var width = html.width(); 43 | html.remove(); 44 | return width; 45 | } 46 | 47 | InlineSnippetForm.prototype = new InlineWidget(); 48 | InlineSnippetForm.prototype.constructor = InlineSnippetForm; 49 | InlineSnippetForm.prototype.parentClass = InlineWidget.prototype; 50 | 51 | InlineSnippetForm.prototype.props = null; 52 | InlineSnippetForm.prototype.$wrapperDiv = null; 53 | InlineSnippetForm.prototype.$form = null; 54 | InlineSnippetForm.prototype.$insert = null; 55 | 56 | InlineSnippetForm.prototype.load = function (hostEditor) { 57 | 58 | var htmlOutput = this.snippet, 59 | x; 60 | 61 | this.parentClass.load.call(this, hostEditor); 62 | this.$form = $('
'); 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 | 16 | -------------------------------------------------------------------------------- /templates/settings-dialog.html: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /templates/snippets-table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{#snippets}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{/snippets}} 18 |
NameDescriptionTriggerUsageSource
{{name}}{{description}}{{trigger}}{{usage}}{{source}}
--------------------------------------------------------------------------------