├── templates ├── button.html ├── content.html ├── successGistDialog.html ├── gist.html ├── panel.html └── newGistDialog.html ├── readmefiles ├── panel.jpg └── newgist.jpg ├── .gitmodules ├── .gitignore ├── strings.js ├── package.json ├── nls ├── strings.js ├── root │ └── strings.js └── de │ └── strings.js ├── .jshintrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── styles ├── gist-manager.css ├── images │ └── icon_gists.svg └── gist-manager.less └── main.js /templates/button.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /readmefiles/panel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FezVrasta/gist-manager/HEAD/readmefiles/panel.jpg -------------------------------------------------------------------------------- /readmefiles/newgist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FezVrasta/gist-manager/HEAD/readmefiles/newgist.jpg -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "thirdparty/async"] 2 | path = thirdparty/async 3 | url = https://github.com/caolan/async.git 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://git-scm.com/docs/gitignore 2 | # https://help.github.com/articles/ignoring-files 3 | # Example .gitignore files: https://github.com/github/gitignore -------------------------------------------------------------------------------- /templates/content.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 |
7 | {{#gists}} 8 |
9 | {{/gists}} 10 |
11 | -------------------------------------------------------------------------------- /strings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file provides the interface to user visible strings in Brackets. Code that needs 3 | * to display strings should should load this module by calling var Strings = require("strings"). 4 | * The i18n plugin will dynamically load the strings for the right locale and populate 5 | * the exports variable. See nls/root/strings.js for the master file of English strings. 6 | */ 7 | define(function (require, exports, module) { 8 | module.exports = require("i18n!nls/strings"); 9 | }); 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fezvrasta.gist-manager", 3 | "title": "Gist Manager", 4 | "description": "Create and view Github Gists within Brackets. (View > Show Gists Manager)", 5 | "keywords": ["gist", "GitHub", "snippets"], 6 | "homepage": "https://github.com/FezVrasta/gist-manager", 7 | "version": "0.1.1", 8 | "author": "Fez Vrasta (http://www.mywebexpression.com)", 9 | "license": "MIT", 10 | "engines": { 11 | "brackets": ">=0.24.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /templates/successGistDialog.html: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /nls/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @see: https://github.com/adobe/brackets/tree/master/src/extensions/samples/LocalizationExample 3 | */ 4 | define(function (require, exports, module) { 5 | // Code that needs to display user strings should call require("strings") to load 6 | // strings.js. This file will dynamically load strings.js for the specified by bracketes.locale. 7 | // 8 | // Translations for other locales should be placed in nls/>/strings.js 9 | // Localization is provided via the i18n plugin. 10 | // All other bundles for languages need to add a prefix to the exports below so i18n can find them. 11 | module.exports = { 12 | root: true, 13 | "de": true 14 | }; 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /templates/gist.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | {{#public}}{{PUBLIC}}{{/public}}{{^public}}{{SECRET}}{{/public}}{{description}} 8 |
9 | 10 | {{#files}} 11 |
12 |
13 | {{filename}} 14 |
15 |
16 |
{{content}}
17 |
18 |
19 | {{/files}} 20 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": false, 6 | "es3": false, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonbsp": true, 16 | "nonew": true, 17 | "plusplus": false, 18 | "quotmark": "double", 19 | "undef": true, 20 | "unused": true, 21 | "strict": false, 22 | "trailing": true, 23 | "maxparams": 5, 24 | "maxdepth": 5, 25 | "maxstatements": 50, 26 | "maxlen": 150, 27 | 28 | "eqnull": true, 29 | 30 | "browser": false, 31 | "devel": false, 32 | "node": true, 33 | 34 | "white": true, 35 | 36 | "globals": { 37 | "$": true, 38 | "document": true, 39 | "brackets": true, 40 | "define": true, 41 | "Mustache": true, 42 | "window": true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.1 2 | * Added Gist Manager button into main toolbar (you can disbled it from preferences file) by [malas34](https://github.com/malas34) 3 | 4 | ## 0.1.0 5 | * First stable release, fix of various critical bugs. 6 | 7 | ## 0.0.8 8 | * Now is possible to delete gists. 9 | 10 | ## 0.0.7 11 | * Added support for [GitHub authorization tokens](https://github.com/settings/applications). 12 | * Auth tokens are saved locally and can be loaded writing the owner's username in the username field. 13 | 14 | ## 0.0.6 15 | * Added option in menu to fastly create a Gist with the content of the current document. 16 | * Gists are now listed showing the first 15 chars of the description (if provided). 17 | * Added tool to filter gists by keywords. 18 | 19 | ## 0.0.5 20 | * Added Gist creation support (anonymous, public, secret) 21 | 22 | ## 0.0.4 23 | * Added support for localization. 24 | * Now is possible load public gists, user's public gists and user's secret gists. 25 | * Added buttons to download gist or open in browser 26 | -------------------------------------------------------------------------------- /templates/panel.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | Gist Manager 5 |
6 | 7 | 8 | 9 | {{#auths}} 10 | 13 | 14 | 15 |
16 | 17 | 18 | × 19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 FezVrasta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /nls/root/strings.js: -------------------------------------------------------------------------------- 1 | /*jshint maxlen:false */ 2 | 3 | define({ 4 | SHOW_GIST_MANAGER: "Show Gist Manager", 5 | LOAD_GISTS: "Load Gists", 6 | NEW_GIST: "New Gist", 7 | GIST_FROM_CURRENT_FILE: "New Gist from current file", 8 | USERNAME: "username", 9 | PASSWORD: "password/token", 10 | FILTER: "Filter gists by keywords...", 11 | DOWNLOAD_GIST: "Download Gist", 12 | OPEN_IN_BROWSER: "Open in browser", 13 | PUBLIC: "Public", 14 | SECRET: "Secret", 15 | CREATE_NEW_GIST: "Create new Gist", 16 | ADD_FILE: "Add file", 17 | CANCEL: "Cancel", 18 | CREATE_PUBLIC_GIST: "Create public Gist", 19 | CREATE_SECRET_GIST: "Create secret Gist", 20 | GIST_DESCRIPTION_HERE: "Write your Gist description here...", 21 | FILENAME_HERE: "Write here an optional file name...", 22 | GIST_CONTENT_HERE: "Write your Gist content here...", 23 | CREATION_ERROR: "Failed to create Gist", 24 | CREATION_SUCCESS: "Gist successfully created", 25 | CLOSE: "Close", 26 | LOADING_ERROR: "Loading error", 27 | DELETE_GIST: "Delete Gist", 28 | DELETING_ERROR: "Error during Gist deletion!" 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /nls/de/strings.js: -------------------------------------------------------------------------------- 1 | /*jshint maxlen:false */ 2 | 3 | define({ 4 | SHOW_GIST_MANAGER: "Gist Manager anzeigen", 5 | LOAD_GISTS: "Lade Gists", 6 | NEW_GIST: "Neues Gist", 7 | GIST_FROM_CURRENT_FILE: "Neues Gist aus aktueller Datei", 8 | USERNAME: "benutzername", 9 | PASSWORD: "passwort/token", 10 | FILTER: "Filtere Gists nach Keywords...", 11 | DOWNLOAD_GIST: "Downloade Gist", 12 | OPEN_IN_BROWSER: "Im Browser öffnen", 13 | PUBLIC: "Öffentlich", 14 | SECRET: "Geheim", 15 | CREATE_NEW_GIST: "Neues Gist erstellen", 16 | ADD_FILE: "Datei hinzufügen", 17 | CANCEL: "Abbrechen", 18 | CREATE_PUBLIC_GIST: "Öffentliches Gist erstellen", 19 | CREATE_SECRET_GIST: "Geheimes Gist erstellen", 20 | GIST_DESCRIPTION_HERE: "Beschreibung deines Gist", 21 | FILENAME_HERE: "Dateiname (optional)", 22 | GIST_CONTENT_HERE: "Inhalt deines Gist", 23 | CREATION_ERROR: "Erstellung des Gist fehlgeschlagen", 24 | CREATION_SUCCESS: "Gist erfolgreich erstellt", 25 | CLOSE: "Schließen", 26 | LOADING_ERROR: "Fehler beim Laden", 27 | DELETE_GIST: "Gist löschen", 28 | DELETING_ERROR: "Fehler beim Löschen deines Gist!" 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /templates/newGistDialog.html: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gist Manager 2 | 3 | Gist Manager is a [Brackets](https://github.com/adobe/brackets) and [Adobe Edge Code](http://html.adobe.com/edge/code/) extension which allows you to search, consult and create Gists from GitHub. 4 | 5 | It has lot of features and every help to improve them is very welcome. 6 | 7 | If you find a bug please [open an issue](https://github.com/FezVrasta/gist-manager/issues)! 8 | 9 | Do you like this project? Star it and [offer me some coffe](https://www.gittip.com/FezVrasta/)! 10 | 11 | 12 | 13 | 14 | ## Usage: 15 | To show Gist Manager click on **`View > Show Gist Manager`**, the panel will appear on bottom of Brackets. 16 | Writing an username of GitHub in the first field and clicking **Load Gists** will load the list of public Gists of the choosen user. 17 | You may specify the password to load even the secret Gists. 18 | 19 | To publish an anonymous Gist click on **New gist** and a dialog will guide you. 20 | If you have specified an username and password, clicking **New gist** will create a Gist in your account, and you'll be able to choose if make it public or secret. 21 | 22 | After you've published a new Gist a modal popup will appear letting you copy the Gist URL or open it in your browser. 23 | 24 | ## Supported features: 25 | - Create anonymous, public and secret Gists 26 | - Support for multi-file Gists 27 | - Creation of Gists starting from selected text 28 | - Load public Gists of a specific GitHub user. 29 | - Load public and secret Gists of a specific GitHub user (providing password). 30 | - Open Gist in browser 31 | - Delete Gist 32 | - Download Gist 33 | - Search Gist 34 | - Login with [application tokens](https://github.com/settings/applications). 35 | - Remember application tokens. 36 | 37 | ## To do: 38 | These are the features in the to do list of this extension: 39 | - Edit Gists 40 | - View and manage forks 41 | 42 | 43 | ## Screenshots: 44 | ![panel](/readmefiles/panel.jpg) 45 | ![newgist](/readmefiles/newgist.jpg) 46 | -------------------------------------------------------------------------------- /styles/gist-manager.css: -------------------------------------------------------------------------------- 1 | #gist-manager-button{background:url(images/icon_gists.svg ) no-repeat center top transparent}#gist-manager-button.active{background-position:center bottom}#gist-manager .toolbar .title{margin-right:20px}#gist-manager .toolbar #filter-content{position:absolute;right:40px;top:5px}#gist-manager .toolbar .btn-group input{margin:0px;width:100px;border-right:0;padding-top:3px;border-radius:4px 0 0 4px}#gist-manager .toolbar .btn-group input:nth-child(2){border-radius:0}#gist-manager .gist-manager-content{overflow:hidden}#gist-manager .gist-manager-content .list{float:left;width:200px;background-color:#FFFFFF;height:100%;box-shadow:inset -1px 1px 0 #ddd;overflow-y:auto}#gist-manager .gist-manager-content .list .list-group{margin:0}#gist-manager .gist-manager-content .list .list-group .list-group-item{list-style:none}#gist-manager .gist-manager-content .list .list-group .list-group-item a{position:relative;display:block;padding:7px 15px;margin-bottom:-1px;border:1px solid #DDDDDD}#gist-manager .gist-manager-content .list .list-group .list-group-item a:hover{background-color:#F5F5F5;text-decoration:none}#gist-manager .gist-manager-content .list .list-group .list-group-item a.active{color:#FFFFFF;background-color:#428BCA;border-color:#428BCA}#gist-manager .gist-manager-content .viewer{margin-left:200px;padding:10px;overflow-y:scroll;height:100%;box-sizing:border-box}#gist-manager .gist-manager-content .viewer .gist{display:none}#gist-manager .gist-manager-content .viewer .gist .gist-overview{overflow:hidden;margin-bottom:8px}#gist-manager .gist-manager-content .viewer .gist .gist-overview summary{padding:0 10px 10px 10px}#gist-manager .gist-manager-content .viewer .gist .gist-overview summary .label{text-transform:capitalize;margin-right:10px;border:1px solid #d7d7d7;color:#d7d7d7;padding:3px 6px 2px;border-radius:3px;background:#F3F3F3}#gist-manager .gist-manager-content .viewer .gist .gist-overview summary .label.secret{border-width:0px;background-color:#f8eec7;color:#A1882B}#gist-manager .gist-manager-content .viewer .gist .gist-overview .btn-group{float:right}#gist-manager .gist-manager-content .viewer .gist .file{margin-bottom:15px;border:1px solid #DDDDDD;border-radius:3px}#gist-manager .gist-manager-content .viewer .gist .file .meta{padding:5px 10px;font-size:12px;text-align:left;color:#555;text-shadow:0 1px 0 #FFFFFF;border-bottom:1px solid #D8D8D8;background-color:#F7F7F7;border-top-left-radius:3px;border-top-right-radius:3px}#gist-manager .gist-manager-content .viewer .gist .file .blob pre{border-radius:0;border:0;margin:0}#gist-manager .gist-manager-content .viewer .gist .file .blob pre code{-webkit-user-select:initial;cursor:text}#gist-manager .gist-manager-content .viewer .gist .file:last-child{margin-bottom:0px}#gist-manager .gist-manager-content .viewer .gist .file:first-child{margin-bottom:15px !important}.new-gist-dialog .nav,.success-gist-dialog .nav{border-bottom:1px solid #c3c6c5;margin-bottom:5px}.new-gist-dialog .nav li,.success-gist-dialog .nav li{padding-bottom:1px}.new-gist-dialog .nav li a:hover,.success-gist-dialog .nav li a:hover{background-color:#ECECEC}.new-gist-dialog #prototype-file,.success-gist-dialog #prototype-file{display:none}.new-gist-dialog input,.success-gist-dialog input{width:100%;box-sizing:border-box;padding:15px 7px}.new-gist-dialog textarea,.success-gist-dialog textarea{box-sizing:border-box;width:100%;height:200px} -------------------------------------------------------------------------------- /styles/images/icon_gists.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 23 | 25 | 26 | 27 | 29 | 31 | 32 | 36 | 40 | 41 | 42 | 44 | 46 | 47 | 49 | 50 | 51 | 53 | 55 | 56 | 58 | 59 | -------------------------------------------------------------------------------- /styles/gist-manager.less: -------------------------------------------------------------------------------- 1 | // out: gist-manager.css, compress: true, strictMath: true 2 | 3 | // Variables 4 | @list-width: 200px; 5 | 6 | // Button 7 | #gist-manager-button{ 8 | background: url( images/icon_gists.svg ) no-repeat center top transparent; 9 | &.active{ 10 | background-position: center bottom; 11 | } 12 | } 13 | 14 | // Panel 15 | #gist-manager { 16 | .toolbar { 17 | .title { 18 | margin-right: 20px; 19 | } 20 | #filter-content { 21 | position: absolute; 22 | right: 40px; 23 | top: 5px; 24 | } 25 | .btn-group { 26 | input { 27 | margin: 0px; 28 | width: 100px; 29 | border-right: 0; 30 | padding-top: 3px; 31 | border-radius: 4px 0 0 4px; 32 | &:nth-child(2) { 33 | border-radius: 0; 34 | } 35 | } 36 | } 37 | } 38 | .gist-manager-content { 39 | overflow: hidden; 40 | .list { 41 | float: left; 42 | width: @list-width; 43 | background-color: #FFFFFF; 44 | height: 100%; 45 | box-shadow: inset -1px 1px 0px #DDDDDD; 46 | overflow-y: auto; 47 | .list-group { 48 | margin: 0; 49 | .list-group-item { 50 | list-style: none; 51 | a { 52 | position: relative; 53 | display: block; 54 | padding: 7px 15px; 55 | margin-bottom: -1px; 56 | border: 1px solid #DDDDDD; 57 | &:hover { 58 | background-color: #F5F5F5; 59 | text-decoration: none; 60 | } 61 | &.active { 62 | color: #FFFFFF; 63 | background-color: #428BCA; 64 | border-color: #428BCA; 65 | } 66 | } 67 | } 68 | } 69 | } 70 | .viewer { 71 | margin-left: @list-width; 72 | padding: 10px; 73 | overflow-y: scroll; 74 | height: 100%; 75 | box-sizing: border-box; 76 | .gist { 77 | display: none; 78 | .gist-overview { 79 | overflow: hidden; 80 | margin-bottom: 8px; 81 | summary { 82 | padding: 0 10px 10px 10px; 83 | .label { 84 | text-transform: capitalize; 85 | margin-right: 10px; 86 | border: 1px solid #d7d7d7; 87 | color: #d7d7d7; 88 | padding: 3px 6px 2px; 89 | border-radius: 3px; 90 | background: #F3F3F3; 91 | &.secret { 92 | border-width: 0px; 93 | background-color: #f8eec7; 94 | color: #A1882B; 95 | } 96 | } 97 | } 98 | .btn-group { 99 | float: right; 100 | } 101 | } 102 | .file { 103 | margin-bottom: 15px; 104 | border: 1px solid #DDDDDD; 105 | border-radius: 3px; 106 | .meta { 107 | padding: 5px 10px; 108 | font-size: 12px; 109 | text-align: left; 110 | color: #555; 111 | text-shadow: 0 1px 0 #FFFFFF; 112 | border-bottom: 1px solid #D8D8D8; 113 | background-color: #F7F7F7; 114 | border-top-left-radius: 3px; 115 | border-top-right-radius: 3px; 116 | } 117 | .blob pre{ 118 | border-radius: 0; 119 | border: 0; 120 | margin: 0; 121 | code { 122 | -webkit-user-select: initial; 123 | cursor: text; 124 | } 125 | } 126 | &:last-child { 127 | margin-bottom: 0px; 128 | } 129 | &:first-child { 130 | margin-bottom: 15px !important; 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | // Dialog 139 | .new-gist-dialog, .success-gist-dialog { 140 | 141 | .nav { 142 | border-bottom: 1px solid #c3c6c5; 143 | margin-bottom: 5px; 144 | li { 145 | padding-bottom: 1px; 146 | a:hover { 147 | background-color: #ECECEC; 148 | } 149 | } 150 | } 151 | 152 | #prototype-file { 153 | display: none; 154 | } 155 | 156 | input { 157 | width: 100%; 158 | box-sizing: border-box; 159 | padding: 15px 7px; 160 | } 161 | 162 | textarea { 163 | box-sizing: border-box; 164 | width: 100%; 165 | height: 200px; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /*global define, brackets, $, Mustache, btoa */ 2 | 3 | define(function (require, exports, module) { 4 | "use strict"; 5 | 6 | var panel = require("text!templates/panel.html"), 7 | content = require("text!templates/content.html"), 8 | gist = require("text!templates/gist.html"), 9 | button = require("text!templates/button.html"), 10 | newGistDialog = require("text!templates/newGistDialog.html"), 11 | successGistDialog = require("text!templates/successGistDialog.html"); 12 | 13 | var CommandManager = brackets.getModule("command/CommandManager"), 14 | DocumentManager = brackets.getModule("document/DocumentManager"), 15 | PreferencesManager = brackets.getModule("preferences/PreferencesManager"), 16 | Dialogs = brackets.getModule("widgets/Dialogs"), 17 | EditorManager = brackets.getModule("editor/EditorManager"), 18 | ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), 19 | Menus = brackets.getModule("command/Menus"), 20 | PanelManager = brackets.getModule("view/PanelManager"); 21 | 22 | var Strings = require("strings"); 23 | 24 | var $panel = $(), 25 | $content = $(), 26 | $button = $(), 27 | gists = null; 28 | 29 | var PREFIX = "gist-manager", 30 | TOGGLE_PANEL = PREFIX + ".run", 31 | GIST_FROM_CURRENT_FILE = PREFIX + ".fromfile", 32 | GM_PANEL = PREFIX + ".panel", 33 | NEW_GIST_MENU = PREFIX + ".menu"; 34 | 35 | 36 | // Load preferences var 37 | var prefs = PreferencesManager.getExtensionPrefs(PREFIX); 38 | 39 | var auths = prefs.get("auths") || false; 40 | if (!auths) { 41 | auths = {}; 42 | prefs.set("auths", auths); 43 | prefs.save(); 44 | } 45 | 46 | // If showButton preference is not defined, set it to true 47 | if (prefs.get("showButton") === undefined) { 48 | prefs.set("showButton", true); 49 | prefs.save(); 50 | } 51 | 52 | // Make :contains case insensitive (:containsIN) 53 | $.extend($.expr[":"], { 54 | "containsIN": function(elem, i, match) { 55 | return (elem.textContent || elem.innerText || "").toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0; 56 | } 57 | }); 58 | 59 | // Show or hide Gist Manager panel when called 60 | function _handlePanelToggle() { 61 | 62 | if ($panel.is(":visible")) { 63 | $panel.hide(); 64 | $button.removeClass("active"); 65 | CommandManager.get(TOGGLE_PANEL).setChecked(false); 66 | EditorManager.focusEditor(); 67 | } else { 68 | $panel.show(); 69 | $button.addClass("active"); 70 | CommandManager.get(TOGGLE_PANEL).setChecked(true); 71 | } 72 | EditorManager.resizeEditor(); 73 | } 74 | 75 | 76 | // Render a given Gist inside the Gist Manager panel 77 | function renderGist(gistData) { 78 | 79 | // If the gist we are trying to render is already loded 80 | // don't load it again but just show the cached version 81 | // if is not cached then load it from Gist API 82 | if (!$panel.find("#" + gistData.id).data("loaded")) { 83 | 84 | // Load Gist data and prepare it for Mustache 85 | $.getJSON(gistData.url, function(gistData) { 86 | 87 | // Convert .files from object to array (needed because Mustache is stupid I guess, or maybe I'm) 88 | gistData.files = $.map(gistData.files, function(value) { 89 | return [value]; 90 | }); 91 | 92 | // Render Gist using Mustache 93 | var vars = gistData; 94 | $.extend(vars, Strings); 95 | var $gist = $(Mustache.render(gist, vars)); 96 | 97 | // Inject the rendered Gist in the Gist Manager panel 98 | $panel.find("#" + gistData.id).html($gist).data("loaded", true); 99 | }); 100 | } 101 | 102 | // Move things around to show the selected Gist 103 | $panel 104 | .find(".gist").hide().end() 105 | .find("a").removeClass("active").end() 106 | .find("a[href=#" + gistData.id + "]").addClass("active").end() 107 | .find("#" + gistData.id).show(); 108 | } 109 | 110 | function getAuth(username, password, action) { 111 | 112 | // These will be used in our Ajax call 113 | var url, 114 | headers; 115 | 116 | // Set headers and API URL depending if we are trying to get public gists 117 | // user's public gists or user's public & secret gists 118 | if (username.length && password.length && password.length !== 40) { 119 | // Basic login with username and password 120 | url = "https://api.github.com/gists"; 121 | headers = { "Authorization": "Basic " + btoa(username + ":" + password) }; 122 | } else if (username.length && !password.length) { 123 | // No login but filter by username 124 | url = "https://api.github.com/users/" + username + "/gists"; 125 | headers = { }; 126 | } else if (username.length && password.length == 40) { 127 | // OAuth login with authorization token 128 | url = "https://api.github.com/gists"; 129 | headers = { "Authorization": "token " + password }; 130 | // Store Auth token to Brackets preferences file 131 | auths[username] = {}; 132 | auths[username].username = username; 133 | auths[username].password = password; 134 | prefs.set("auths", auths); 135 | prefs.save(); 136 | } else { 137 | // No login, show public gists 138 | url = "https://api.github.com/gists"; 139 | headers = { }; 140 | } 141 | 142 | if (action === "GET") { 143 | return {"url": url, "headers": headers}; 144 | } else if (action === "DELETE") { 145 | if (headers.Authorization) { 146 | return {"url": "https://api.github.com/gists/", "headers": headers}; 147 | } 148 | } 149 | 150 | } 151 | 152 | // Load list of Gists 153 | // if no username is provided then load the public list 154 | // if username is provided then load the list of the selected username 155 | // if password is provided then load even the secret Gists of the selected user 156 | function loadContent(username, password) { 157 | 158 | // These will be used in our Ajax call 159 | var auth = getAuth(username, password, "GET"); 160 | 161 | $.ajax({ 162 | type: "GET", 163 | url: auth.url, 164 | dataType: "json", 165 | headers: auth.headers, 166 | success: function (data) { 167 | gists = data; 168 | 169 | $.map(gists, function(gist) { 170 | 171 | gist.shortDescription = "gist:" + gist.id; 172 | if (typeof gist.description != null && gist.description != null) { 173 | if (gist.description.length) { 174 | gist.shortDescription = gist.description.substring(0, 20); 175 | } 176 | } 177 | return gist; 178 | 179 | }); 180 | 181 | _renderContent(gists); 182 | }, 183 | error: function (err) { 184 | var response = JSON.parse(err.responseText); 185 | Dialogs.showModalDialog("error-dialog", Strings.LOADING_ERROR, response.message); 186 | console.error("gist-manager:", err); 187 | } 188 | }); 189 | 190 | // Renders a given list of Gists inside the panel 191 | function _renderContent(gists) { 192 | 193 | var vars = {"gists": gists}; 194 | $content = $(Mustache.render(content, vars)); 195 | $panel.find(".gist-manager-content").html($content); 196 | 197 | // Render the first gist 198 | renderGist(gists[0]); 199 | 200 | // Add event handler on the list of Gists 201 | $panel.on("click", ".list-group-item", function(event) { 202 | 203 | gists.forEach( function(gist) { 204 | if (gist.id == $(event.target).data("id")) { 205 | renderGist(gist); 206 | return; 207 | } 208 | }); 209 | 210 | }); 211 | } 212 | } 213 | 214 | function deleteGist(username, password, id) { 215 | 216 | // These will be used in our Ajax call 217 | var auth = getAuth(username, password, "DELETE"); 218 | 219 | if (typeof auth === undefined || auth === undefined) { 220 | Dialogs.showModalDialog("error-dialog", Strings.DELETING_ERROR, "You do not own this Gist, please check your login details."); 221 | return; 222 | } 223 | 224 | $.ajax({ 225 | type: "DELETE", 226 | url: auth.url + id, 227 | dataType: "json", 228 | headers: auth.headers, 229 | success: function () { 230 | $panel.find("#" + id).remove(); 231 | $panel.find("*[data-id=" + id + "]").parent().remove(); 232 | $panel.find(".list li").first().addClass("active").find("a").trigger("click"); 233 | }, 234 | error: function (err) { 235 | var response = JSON.parse(err.responseText); 236 | Dialogs.showModalDialog("error-dialog", Strings.LOADING_ERROR, response.message); 237 | console.error("gist-manager:", err); 238 | } 239 | }); 240 | } 241 | 242 | function filterContent(query) { 243 | 244 | $panel.find(".gist-manager-content .list li").show(); 245 | $panel.find(".gist-manager-content .list li:not(:containsIN('" + query + "'))").hide(); 246 | 247 | } 248 | 249 | // Post a new Gist 250 | function newGist(username, password, entireFile) { 251 | 252 | var content = "", 253 | gistFileName = "", 254 | filename = DocumentManager.getCurrentDocument().file._name, 255 | selection = EditorManager.getCurrentFullEditor().getSelectedText(); 256 | 257 | 258 | if (entireFile) { 259 | content = DocumentManager.getCurrentDocument()._masterEditor.document.file._contents; 260 | } else if (selection.length) { 261 | content = selection; 262 | } 263 | 264 | if (content.length && filename.length) { 265 | gistFileName = filename; 266 | } 267 | 268 | var vars = $.extend({"content": content, "filename": gistFileName, "secret": (username.length && password.length)}, Strings); 269 | 270 | var dialog = Dialogs.showModalDialogUsingTemplate(Mustache.render(newGistDialog, vars)), 271 | $dialog = dialog.getElement(); 272 | 273 | $dialog. 274 | on("click", "#add-file", function() { 275 | $dialog.find("#prototype-file .file").first().clone().appendTo("#files"); 276 | }); 277 | 278 | dialog.done(function (buttonId) { 279 | if (buttonId === "create-public-gist" || buttonId === "create-secret-gist") { 280 | 281 | var url = "https://api.github.com/gists", 282 | headers; 283 | 284 | var gistData = { 285 | "description": $dialog.find("[name=description]").val(), 286 | "public": (buttonId === "create-public-gist"), 287 | "files": { } 288 | }; 289 | 290 | $dialog.find("#files .file").each( function() { 291 | gistData.files[$(this).find(".filename").val()] = {}; 292 | gistData.files[$(this).find(".filename").val()].content = $(this).find(".content").val(); 293 | }); 294 | 295 | // Set header if user wants to be authenticated 296 | if (username.length && password.length) { 297 | headers = { "Authorization": "Basic " + btoa(username + ":" + password) }; 298 | } else { 299 | headers = { }; 300 | } 301 | 302 | $.ajax({ 303 | type: "POST", 304 | url: url, 305 | dataType: "json", 306 | headers: headers, 307 | data: JSON.stringify(gistData), 308 | success: function (response) { 309 | var vars = $.extend(response, Strings); 310 | var dialog = Dialogs.showModalDialogUsingTemplate(Mustache.render(successGistDialog, vars)); 311 | 312 | dialog.done(function (buttonId) { 313 | if (buttonId === "open") { 314 | brackets.app.openURLInDefaultBrowser(response.html_url); 315 | } 316 | }); 317 | 318 | if (username.length && password.length) { 319 | loadContent(username, password); 320 | } 321 | }, 322 | error: function (err) { 323 | var response = JSON.parse(err.responseText); 324 | Dialogs.showModalDialog("error-dialog", Strings.CREATION_ERROR, response.message); 325 | console.error("gist-manager:", err); 326 | } 327 | }); 328 | } 329 | }); 330 | } 331 | 332 | function loadToken(username) { 333 | 334 | if (auths[username]) { 335 | $panel.find("#github-password").val(auths[username].password); 336 | } else { 337 | $panel.find("#github-password").val(""); 338 | } 339 | 340 | } 341 | 342 | function init() { 343 | 344 | // Load compiled CSS of Gist Manager 345 | ExtensionUtils.loadStyleSheet(module, "styles/gist-manager.css"); 346 | 347 | // Add menu option to toggle Gist Manager panel 348 | CommandManager.register(Strings.SHOW_GIST_MANAGER, TOGGLE_PANEL, _handlePanelToggle); 349 | var menu = Menus.getMenu(Menus.AppMenuBar.VIEW_MENU); 350 | menu.addMenuItem(TOGGLE_PANEL, null, Menus.AFTER); 351 | 352 | // Add menu option to create Gist of the current file 353 | CommandManager.register(Strings.GIST_FROM_CURRENT_FILE, GIST_FROM_CURRENT_FILE, function() { 354 | newGist($panel.find("#github-username").val(), $panel.find("#github-password").val(), true); 355 | }); 356 | var editMenu = Menus.getMenu(Menus.AppMenuBar.EDIT_MENU); 357 | editMenu.addMenuItem(GIST_FROM_CURRENT_FILE, null, Menus.AFTER); 358 | 359 | // Add context menu option to create gist 360 | CommandManager.register( 361 | Strings.CREATE_NEW_GIST, 362 | NEW_GIST_MENU, 363 | function() { newGist($panel.find("#github-username").val(), $panel.find("#github-password").val()); } 364 | ); 365 | var contextMenu = Menus.getContextMenu(Menus.ContextMenuIds.EDITOR_MENU); 366 | contextMenu.addMenuItem(NEW_GIST_MENU); 367 | 368 | // Create Gist Manager panel 369 | var vars = Strings, 370 | authsMustache = []; 371 | 372 | $.each(auths, function(auth) { 373 | authsMustache.push(auth); 374 | }); 375 | 376 | $.extend(vars, {"auths": authsMustache}); 377 | 378 | PanelManager.createBottomPanel(GM_PANEL, $(Mustache.render(panel, vars)), 200); 379 | 380 | // Cache selection of Gist Manager panel 381 | $panel = $("#gist-manager"); 382 | 383 | // Add events handler to Gist Manager panel 384 | $panel 385 | .on("click", "#load-gists", function() { 386 | loadContent($panel.find("#github-username").val(), $panel.find("#github-password").val()); 387 | }) 388 | .on("click", "#new-gist", function() { 389 | newGist($panel.find("#github-username").val(), $panel.find("#github-password").val()); 390 | }) 391 | .on("keyup", "#filter-content", function() { 392 | filterContent($panel.find("#filter-content").val()); 393 | }) 394 | .on("keyup", "#github-username", function() { 395 | loadToken($panel.find("#github-username").val()); 396 | }) 397 | .on("click", ".delete-gist", function() { 398 | deleteGist($panel.find("#github-username").val(), $panel.find("#github-password").val(), $(this).attr("data-id")); 399 | }) 400 | .on("click", ".close", _handlePanelToggle); 401 | 402 | // Create button only if required by user settings 403 | if (prefs.get("showButton")) { 404 | // Append button to toolbar 405 | $("#main-toolbar .buttons").append(Mustache.render(button)); 406 | $button = $("#gist-manager-button"); 407 | 408 | // Add events handler to Gist Manager button if required 409 | $(document).on("click", "#gist-manager-button", _handlePanelToggle); 410 | } 411 | } 412 | 413 | init(); 414 | 415 | }); 416 | --------------------------------------------------------------------------------