├── .eslintrc ├── .gitignore ├── Gruntfile.js ├── README.md ├── app.js ├── config ├── dbs.toml ├── default.toml ├── mta │ ├── dns.toml │ ├── domains.toml │ ├── minimta.toml │ ├── plugins │ │ ├── default-headers.toml │ │ ├── email-bounce.toml │ │ ├── mailcast.toml │ │ └── zonemta-limiter.toml │ ├── pools.toml │ ├── queue.toml │ └── zones │ │ ├── default.toml │ │ └── lists.toml ├── user.toml └── verp.toml ├── lib ├── auth.js ├── db.js ├── emails.js ├── initialize.js ├── locales.json ├── maildrop.js ├── mailer.js ├── message-renderer.js ├── message-splitter.js ├── timezones.js ├── tools.js └── tz.json ├── models ├── list.js ├── mail.js ├── message.js ├── settings.js ├── subscriber.js ├── template.js └── user.js ├── package.json ├── plugins └── mailcast.js ├── public ├── ace │ ├── ace.js │ ├── ext-beautify.js │ ├── ext-elastic_tabstops_lite.js │ ├── ext-emmet.js │ ├── ext-error_marker.js │ ├── ext-keybinding_menu.js │ ├── ext-language_tools.js │ ├── ext-linking.js │ ├── ext-modelist.js │ ├── ext-options.js │ ├── ext-searchbox.js │ ├── ext-settings_menu.js │ ├── ext-spellcheck.js │ ├── ext-split.js │ ├── ext-static_highlight.js │ ├── ext-statusbar.js │ ├── ext-textarea.js │ ├── ext-themelist.js │ ├── ext-whitespace.js │ ├── keybinding-emacs.js │ ├── keybinding-vim.js │ ├── mode-abap.js │ ├── mode-abc.js │ ├── mode-actionscript.js │ ├── mode-ada.js │ ├── mode-apache_conf.js │ ├── mode-applescript.js │ ├── mode-asciidoc.js │ ├── mode-asl.js │ ├── mode-assembly_x86.js │ ├── mode-autohotkey.js │ ├── mode-batchfile.js │ ├── mode-bro.js │ ├── mode-c9search.js │ ├── mode-c_cpp.js │ ├── mode-cirru.js │ ├── mode-clojure.js │ ├── mode-cobol.js │ ├── mode-coffee.js │ ├── mode-coldfusion.js │ ├── mode-csharp.js │ ├── mode-csound_document.js │ ├── mode-csound_orchestra.js │ ├── mode-csound_score.js │ ├── mode-csp.js │ ├── mode-css.js │ ├── mode-curly.js │ ├── mode-d.js │ ├── mode-dart.js │ ├── mode-diff.js │ ├── mode-django.js │ ├── mode-dockerfile.js │ ├── mode-dot.js │ ├── mode-drools.js │ ├── mode-edifact.js │ ├── mode-eiffel.js │ ├── mode-ejs.js │ ├── mode-elixir.js │ ├── mode-elm.js │ ├── mode-erlang.js │ ├── mode-forth.js │ ├── mode-fortran.js │ ├── mode-ftl.js │ ├── mode-gcode.js │ ├── mode-gherkin.js │ ├── mode-gitignore.js │ ├── mode-glsl.js │ ├── mode-gobstones.js │ ├── mode-golang.js │ ├── mode-graphqlschema.js │ ├── mode-groovy.js │ ├── mode-haml.js │ ├── mode-handlebars.js │ ├── mode-haskell.js │ ├── mode-haskell_cabal.js │ ├── mode-haxe.js │ ├── mode-hjson.js │ ├── mode-html.js │ ├── mode-html_elixir.js │ ├── mode-html_ruby.js │ ├── mode-ini.js │ ├── mode-io.js │ ├── mode-jack.js │ ├── mode-jade.js │ ├── mode-java.js │ ├── mode-javascript.js │ ├── mode-json.js │ ├── mode-jsoniq.js │ ├── mode-jsp.js │ ├── mode-jssm.js │ ├── mode-jsx.js │ ├── mode-julia.js │ ├── mode-kotlin.js │ ├── mode-latex.js │ ├── mode-less.js │ ├── mode-liquid.js │ ├── mode-lisp.js │ ├── mode-livescript.js │ ├── mode-logiql.js │ ├── mode-lsl.js │ ├── mode-lua.js │ ├── mode-luapage.js │ ├── mode-lucene.js │ ├── mode-makefile.js │ ├── mode-markdown.js │ ├── mode-mask.js │ ├── mode-matlab.js │ ├── mode-maze.js │ ├── mode-mel.js │ ├── mode-mixal.js │ ├── mode-mushcode.js │ ├── mode-mysql.js │ ├── mode-nix.js │ ├── mode-nsis.js │ ├── mode-objectivec.js │ ├── mode-ocaml.js │ ├── mode-pascal.js │ ├── mode-perl.js │ ├── mode-pgsql.js │ ├── mode-php.js │ ├── mode-pig.js │ ├── mode-plain_text.js │ ├── mode-powershell.js │ ├── mode-praat.js │ ├── mode-prolog.js │ ├── mode-properties.js │ ├── mode-protobuf.js │ ├── mode-python.js │ ├── mode-r.js │ ├── mode-razor.js │ ├── mode-rdoc.js │ ├── mode-red.js │ ├── mode-redshift.js │ ├── mode-rhtml.js │ ├── mode-rst.js │ ├── mode-ruby.js │ ├── mode-rust.js │ ├── mode-sass.js │ ├── mode-scad.js │ ├── mode-scala.js │ ├── mode-scheme.js │ ├── mode-scss.js │ ├── mode-sh.js │ ├── mode-sjs.js │ ├── mode-smarty.js │ ├── mode-snippets.js │ ├── mode-soy_template.js │ ├── mode-space.js │ ├── mode-sparql.js │ ├── mode-sql.js │ ├── mode-sqlserver.js │ ├── mode-stylus.js │ ├── mode-svg.js │ ├── mode-swift.js │ ├── mode-tcl.js │ ├── mode-tex.js │ ├── mode-text.js │ ├── mode-textile.js │ ├── mode-toml.js │ ├── mode-tsx.js │ ├── mode-turtle.js │ ├── mode-twig.js │ ├── mode-typescript.js │ ├── mode-vala.js │ ├── mode-vbscript.js │ ├── mode-velocity.js │ ├── mode-verilog.js │ ├── mode-vhdl.js │ ├── mode-wollok.js │ ├── mode-xml.js │ ├── mode-xquery.js │ ├── mode-yaml.js │ ├── snippets │ │ ├── abap.js │ │ ├── abc.js │ │ ├── actionscript.js │ │ ├── ada.js │ │ ├── apache_conf.js │ │ ├── applescript.js │ │ ├── asciidoc.js │ │ ├── asl.js │ │ ├── assembly_x86.js │ │ ├── autohotkey.js │ │ ├── batchfile.js │ │ ├── bro.js │ │ ├── c9search.js │ │ ├── c_cpp.js │ │ ├── cirru.js │ │ ├── clojure.js │ │ ├── cobol.js │ │ ├── coffee.js │ │ ├── coldfusion.js │ │ ├── csharp.js │ │ ├── csound_document.js │ │ ├── csound_orchestra.js │ │ ├── csound_score.js │ │ ├── csp.js │ │ ├── css.js │ │ ├── curly.js │ │ ├── d.js │ │ ├── dart.js │ │ ├── diff.js │ │ ├── django.js │ │ ├── dockerfile.js │ │ ├── dot.js │ │ ├── drools.js │ │ ├── edifact.js │ │ ├── eiffel.js │ │ ├── ejs.js │ │ ├── elixir.js │ │ ├── elm.js │ │ ├── erlang.js │ │ ├── forth.js │ │ ├── fortran.js │ │ ├── ftl.js │ │ ├── gcode.js │ │ ├── gherkin.js │ │ ├── gitignore.js │ │ ├── glsl.js │ │ ├── gobstones.js │ │ ├── golang.js │ │ ├── graphqlschema.js │ │ ├── groovy.js │ │ ├── haml.js │ │ ├── handlebars.js │ │ ├── haskell.js │ │ ├── haskell_cabal.js │ │ ├── haxe.js │ │ ├── hjson.js │ │ ├── html.js │ │ ├── html_elixir.js │ │ ├── html_ruby.js │ │ ├── ini.js │ │ ├── io.js │ │ ├── jack.js │ │ ├── jade.js │ │ ├── java.js │ │ ├── javascript.js │ │ ├── json.js │ │ ├── jsoniq.js │ │ ├── jsp.js │ │ ├── jssm.js │ │ ├── jsx.js │ │ ├── julia.js │ │ ├── kotlin.js │ │ ├── latex.js │ │ ├── less.js │ │ ├── liquid.js │ │ ├── lisp.js │ │ ├── livescript.js │ │ ├── logiql.js │ │ ├── lsl.js │ │ ├── lua.js │ │ ├── luapage.js │ │ ├── lucene.js │ │ ├── makefile.js │ │ ├── markdown.js │ │ ├── mask.js │ │ ├── matlab.js │ │ ├── maze.js │ │ ├── mel.js │ │ ├── mixal.js │ │ ├── mushcode.js │ │ ├── mysql.js │ │ ├── nix.js │ │ ├── nsis.js │ │ ├── objectivec.js │ │ ├── ocaml.js │ │ ├── pascal.js │ │ ├── perl.js │ │ ├── pgsql.js │ │ ├── php.js │ │ ├── pig.js │ │ ├── plain_text.js │ │ ├── powershell.js │ │ ├── praat.js │ │ ├── prolog.js │ │ ├── properties.js │ │ ├── protobuf.js │ │ ├── python.js │ │ ├── r.js │ │ ├── razor.js │ │ ├── rdoc.js │ │ ├── red.js │ │ ├── redshift.js │ │ ├── rhtml.js │ │ ├── rst.js │ │ ├── ruby.js │ │ ├── rust.js │ │ ├── sass.js │ │ ├── scad.js │ │ ├── scala.js │ │ ├── scheme.js │ │ ├── scss.js │ │ ├── sh.js │ │ ├── sjs.js │ │ ├── smarty.js │ │ ├── snippets.js │ │ ├── soy_template.js │ │ ├── space.js │ │ ├── sparql.js │ │ ├── sql.js │ │ ├── sqlserver.js │ │ ├── stylus.js │ │ ├── svg.js │ │ ├── swift.js │ │ ├── tcl.js │ │ ├── tex.js │ │ ├── text.js │ │ ├── textile.js │ │ ├── toml.js │ │ ├── tsx.js │ │ ├── turtle.js │ │ ├── twig.js │ │ ├── typescript.js │ │ ├── vala.js │ │ ├── vbscript.js │ │ ├── velocity.js │ │ ├── verilog.js │ │ ├── vhdl.js │ │ ├── wollok.js │ │ ├── xml.js │ │ ├── xquery.js │ │ └── yaml.js │ ├── theme-ambiance.js │ ├── theme-chaos.js │ ├── theme-chrome.js │ ├── theme-clouds.js │ ├── theme-clouds_midnight.js │ ├── theme-cobalt.js │ ├── theme-crimson_editor.js │ ├── theme-dawn.js │ ├── theme-dracula.js │ ├── theme-dreamweaver.js │ ├── theme-eclipse.js │ ├── theme-github.js │ ├── theme-gob.js │ ├── theme-gruvbox.js │ ├── theme-idle_fingers.js │ ├── theme-iplastic.js │ ├── theme-katzenmilch.js │ ├── theme-kr_theme.js │ ├── theme-kuroir.js │ ├── theme-merbivore.js │ ├── theme-merbivore_soft.js │ ├── theme-mono_industrial.js │ ├── theme-monokai.js │ ├── theme-pastel_on_dark.js │ ├── theme-solarized_dark.js │ ├── theme-solarized_light.js │ ├── theme-sqlserver.js │ ├── theme-terminal.js │ ├── theme-textmate.js │ ├── theme-tomorrow.js │ ├── theme-tomorrow_night.js │ ├── theme-tomorrow_night_blue.js │ ├── theme-tomorrow_night_bright.js │ ├── theme-tomorrow_night_eighties.js │ ├── theme-twilight.js │ ├── theme-vibrant_ink.js │ ├── theme-xcode.js │ ├── worker-coffee.js │ ├── worker-css.js │ ├── worker-html.js │ ├── worker-javascript.js │ ├── worker-json.js │ ├── worker-lua.js │ ├── worker-php.js │ ├── worker-xml.js │ └── worker-xquery.js ├── editor.js ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── fonts │ ├── open-iconic.eot │ ├── open-iconic.otf │ ├── open-iconic.svg │ ├── open-iconic.ttf │ └── open-iconic.woff ├── jquery-ui │ ├── jquery-ui.css │ └── jquery-ui.min.js └── quill │ ├── examples │ ├── bubble.html │ ├── full.html │ └── snow.html │ ├── quill.bubble.css │ ├── quill.core.css │ ├── quill.core.js │ ├── quill.js │ ├── quill.min.js │ ├── quill.min.js.map │ └── quill.snow.css ├── routes ├── account │ ├── index.js │ └── settings │ │ └── index.js ├── archive │ └── index.js ├── index.js ├── lists │ ├── index.js │ └── subscribers │ │ └── index.js ├── messages │ └── index.js ├── subscribers │ └── index.js ├── templates │ └── index.js └── users │ └── index.js ├── server.js ├── services ├── bouncer.js ├── minimta.js └── renderer.js ├── setup ├── etc │ ├── logrotate.d │ │ └── mailcast │ ├── rsyslog.d │ │ └── 25-mailcast.conf │ ├── systemd │ │ └── system │ │ │ └── mailcast.service │ └── tmpfiles.d │ │ └── mailcast.conf ├── indexes.yaml └── install.sh ├── sources ├── .eslintrc ├── default-template.html ├── index.js ├── js │ ├── actions.js │ ├── mailcast.js │ └── message.js └── sass │ ├── bootstrap-litera.scss │ ├── bootstrap-lux.scss │ ├── bootstrap-pulse.scss │ ├── bootstrap-sandstone.scss │ ├── bootstrap-simplex.scss │ ├── bootstrap-sketchy.scss │ ├── email.scss │ ├── index.scss │ └── open-iconic-bootstrap.scss ├── views ├── account │ ├── email-validated.pug │ ├── join.pug │ ├── login.pug │ ├── recover.pug │ ├── reset.pug │ ├── set-password.pug │ └── settings │ │ ├── password.pug │ │ ├── profile.pug │ │ ├── settings-header.pug │ │ ├── settings-menu.pug │ │ └── site.pug ├── archive │ └── view.pug ├── edit.pug ├── emails │ ├── README.md │ ├── account-recovery │ │ ├── html.pug │ │ ├── subject.pug │ │ └── text.pug │ ├── email-validation │ │ ├── html.pug │ │ ├── subject.pug │ │ └── text.pug │ ├── subscriber-change │ │ ├── html.pug │ │ ├── subject.pug │ │ └── text.pug │ ├── subscriber-confirm │ │ ├── html.pug │ │ ├── subject.pug │ │ └── text.pug │ ├── subscriptions-token │ │ ├── html.pug │ │ ├── subject.pug │ │ └── text.pug │ └── welcome │ │ ├── html.pug │ │ ├── subject.pug │ │ └── text.pug ├── error.pug ├── help.pug ├── index.pug ├── layouts │ ├── email.pug │ ├── main.pug │ └── subscribers.pug ├── lists │ ├── add.pug │ ├── edit.pug │ ├── fields.pug │ ├── index.pug │ ├── list-form.pug │ ├── list-header.pug │ ├── list-main-header.pug │ ├── list-menu.pug │ ├── subscribers │ │ ├── add.pug │ │ ├── edit.pug │ │ └── form.pug │ └── view.pug ├── messages │ ├── add.pug │ ├── edit.pug │ ├── index.pug │ ├── message-form.pug │ ├── message-header.pug │ ├── message-menu.pug │ ├── message │ │ ├── header.pug │ │ ├── menu.pug │ │ └── view.pug │ ├── select-list.pug │ └── settings.pug ├── mixins.pug ├── partials │ ├── flash-messages.pug │ └── main-menu.pug ├── subscribers │ ├── change.pug │ ├── confirm.pug │ ├── edit.pug │ ├── error.pug │ ├── form.pug │ ├── list.pug │ ├── subscribe.pug │ ├── subscribed.pug │ ├── subscriptions.pug │ ├── unsubscribe.pug │ └── unsubscribed.pug ├── templates │ ├── add.pug │ ├── edit.pug │ ├── index.pug │ ├── template-form.pug │ ├── template-header.pug │ └── template-menu.pug └── users │ ├── add.pug │ ├── edit.pug │ ├── index.pug │ ├── settings.pug │ ├── user-form.pug │ ├── user-header.pug │ └── user-menu.pug └── webpack.config.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": 0, 4 | "global-require": 0, 5 | "no-await-in-loop": 0 6 | }, 7 | "extends": "nodemailer", 8 | "parserOptions": { 9 | "ecmaVersion": 2017 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | public/css 4 | public/app 5 | config/development.toml 6 | package-lock.json 7 | 8 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sass = require('node-sass'); 4 | 5 | module.exports = function(grunt) { 6 | // Project configuration. 7 | grunt.initConfig({ 8 | eslint: { 9 | all: ['lib/**/*.js', 'routes/**/*.js', 'app.js', 'server.js', 'Gruntfile.js'] 10 | }, 11 | 12 | sass: { 13 | options: { 14 | implementation: sass, 15 | sourceMap: true 16 | }, 17 | dist: { 18 | files: { 19 | 'public/css/styles.css': 'sources/sass/index.scss', 20 | 'public/css/email.css': 'sources/sass/email.scss' 21 | } 22 | } 23 | } 24 | }); 25 | 26 | // Load the plugin(s) 27 | grunt.loadNpmTasks('grunt-eslint'); 28 | grunt.loadNpmTasks('grunt-sass'); 29 | 30 | // Tasks 31 | grunt.registerTask('default', ['eslint']); 32 | }; 33 | -------------------------------------------------------------------------------- /config/dbs.toml: -------------------------------------------------------------------------------- 1 | # redis connection string for Express sessions 2 | redis="redis://127.0.0.1:6379/7" 3 | 4 | # main database 5 | mongo="mongodb://127.0.0.1:27017/mailcast" 6 | sender="minimta" 7 | -------------------------------------------------------------------------------- /config/default.toml: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Process title 4 | title="mailcast" 5 | processes=2 6 | 7 | # Can be overriden in the UI by admin 8 | appname="Mailcast" 9 | 10 | [process] 11 | # @include "user.toml" 12 | 13 | [www] 14 | secret="secret cat" 15 | postsize="5MB" 16 | host=false # local ip address to bind to 17 | port=3002 18 | proxy=false # set to true if using CloudFlare etc or behind Nginx/Apache 19 | secure=false # if true uses secure cookies 20 | #baseUrl=false # set to default application url, can be overriden in web interface 21 | 22 | [log] 23 | level="silly" 24 | www=':remote-addr [:date[iso]] :site-user ":method :url HTTP/:http-version" :status :res[content-length]' 25 | 26 | [dbs] 27 | # @include "dbs.toml" 28 | 29 | [mta] 30 | # Load MTA config 31 | # @include "mta/minimta.toml" 32 | 33 | [verp] 34 | # Load VERP MX config 35 | # @include "verp.toml" 36 | 37 | [upgrade] 38 | checkInterval=3600 # seconds 39 | -------------------------------------------------------------------------------- /config/mta/dns.toml: -------------------------------------------------------------------------------- 1 | # If true then caches DNS results to Redis 2 | caching=true 3 | cahceTTL=600 # TTL of cached dns keys in seconds 4 | 5 | # Define nameservers to use (IP addresses only). If using a local DNS cache server, then set caching=false 6 | nameservers=[] 7 | 8 | #caching=false 9 | #nameservers=["127.0.0.1"] 10 | 11 | blockDomains=[] 12 | 13 | # If true then messages to local interfaces are blocked (eg. you can not send to username@localhost) 14 | blockLocalAddresses=true 15 | -------------------------------------------------------------------------------- /config/mta/domains.toml: -------------------------------------------------------------------------------- 1 | # Add domain specific settings here 2 | 3 | [default] 4 | maxConnections=5 5 | 6 | ["gmail.com"] 7 | maxConnections=10 8 | -------------------------------------------------------------------------------- /config/mta/minimta.toml: -------------------------------------------------------------------------------- 1 | # This is the main config file for MTA 2 | 3 | name="MailcastMTA" 4 | 5 | # Process identifier 6 | ident="minimta" 7 | 8 | # @include "../user.toml" 9 | 10 | [log] 11 | # Logging options 12 | level="info" # "silly" "info" "error" 13 | remote=false 14 | 15 | [dbs] 16 | # MongoDB and Redis connection options 17 | # @include "../dbs.toml" 18 | 19 | [queue] 20 | # @include "queue.toml" 21 | 22 | [dns] 23 | # @include "dns.toml" 24 | 25 | [api] 26 | port=12080 27 | maildrop=true 28 | 29 | [smtpInterfaces] 30 | [smtpInterfaces.feeder] 31 | enabled=false 32 | 33 | [plugins] 34 | # @include "plugins/*.toml" 35 | 36 | [pools] 37 | # @include "pools.toml" 38 | 39 | [zones] 40 | # @include "zones/*.toml" 41 | bounces=false 42 | 43 | [domainConfig] 44 | # @include "domains.toml" 45 | -------------------------------------------------------------------------------- /config/mta/plugins/default-headers.toml: -------------------------------------------------------------------------------- 1 | ["core/default-headers"] 2 | enabled=["receiver", "main", "sender"] 3 | # Which missing headers to add 4 | addMissing=["message-id", "date"] 5 | # If true then delays messages with future Date: headers until that time has arrived 6 | futureDate=false 7 | # Add X-Originating-IP header 8 | xOriginatingIP=true 9 | -------------------------------------------------------------------------------- /config/mta/plugins/email-bounce.toml: -------------------------------------------------------------------------------- 1 | ["core/email-bounce"] 2 | enabled=false 3 | -------------------------------------------------------------------------------- /config/mta/plugins/mailcast.toml: -------------------------------------------------------------------------------- 1 | ["mailcast"] 2 | enabled=["receiver", "main", "sender"] 3 | 4 | # @include "../../dbs.toml" 5 | -------------------------------------------------------------------------------- /config/mta/plugins/zonemta-limiter.toml: -------------------------------------------------------------------------------- 1 | # plugins/zonemta-limiter.toml 2 | ["modules/zonemta-limiter"] 3 | enabled = ["sender"] 4 | prefix = "zl:" 5 | 6 | debug = false # if true, then errors are only logged but messages are not dropped 7 | 8 | # max 250 messages in half an hour 9 | limit = 250 10 | windowSize = 1800 11 | -------------------------------------------------------------------------------- /config/mta/pools.toml: -------------------------------------------------------------------------------- 1 | 2 | # List local IP addresses that can be used for outbound tcp connections 3 | # Server process must be able to locally bind to these addresses 4 | default=["0.0.0.0", "::"] 5 | 6 | # or alternatively, to provide hostnames as well: 7 | # 8 | #[[default]] 9 | #address="1.2.3.4" 10 | #name="ip-1.hostname" 11 | # 12 | #[[default]] 13 | #address="1.2.3.5" 14 | #name="ip-2.hostname" 15 | # 16 | -------------------------------------------------------------------------------- /config/mta/queue.toml: -------------------------------------------------------------------------------- 1 | 2 | # Every instance of ZoneMTA that uses the same database backend 3 | # must have its own unique instance id set. If you never plan to 4 | # run more than a single ZoneMTA instance then you do not need to 5 | # change this value 6 | instanceId="default" 7 | 8 | # Collection name for message queue 9 | collection="queue" 10 | 11 | # Bucket name for the GridStore storage 12 | gfs="mail" 13 | 14 | # Set to true if you do not care about cleaning up the remains of broken transactions 15 | disableGC=false 16 | -------------------------------------------------------------------------------- /config/mta/zones/default.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | preferIPv6=false 3 | ignoreIPv6=true 4 | processes=1 5 | connections=5 6 | pool="default" 7 | -------------------------------------------------------------------------------- /config/mta/zones/lists.toml: -------------------------------------------------------------------------------- 1 | [lists] 2 | preferIPv6=false 3 | ignoreIPv6=true 4 | processes=2 5 | connections=5 6 | pool="default" 7 | -------------------------------------------------------------------------------- /config/user.toml: -------------------------------------------------------------------------------- 1 | # downgrade to following user after binding ports 2 | #user="www-data" 3 | #group="www-data" 4 | -------------------------------------------------------------------------------- /config/verp.toml: -------------------------------------------------------------------------------- 1 | port=2525 2 | host="*" 3 | -------------------------------------------------------------------------------- /lib/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let userModel = require('../models/user'); 4 | const gravatarUrl = require('gravatar-url'); 5 | 6 | module.exports.loadUserData = async (req, res, next) => { 7 | if (!req.session.uid) { 8 | req.user = res.locals.user = false; 9 | return next(); 10 | } 11 | 12 | let userData = await userModel.get(req.session.uid, { 13 | _id: true, 14 | name: true, 15 | email: true, 16 | status: true, 17 | emailValidated: true, 18 | locale: true, 19 | tz: true 20 | }); 21 | 22 | if (!userData) { 23 | req.user = res.locals.user = false; 24 | return next(); 25 | } 26 | 27 | userData.gravatar = gravatarUrl(userData.email, { 28 | size: 20, 29 | // 404, mm, identicon, monsterid, wavatar, retro, blank 30 | default: 'identicon' 31 | }); 32 | 33 | req.user = res.locals.user = userData; 34 | next(); 35 | }; 36 | -------------------------------------------------------------------------------- /lib/db.js: -------------------------------------------------------------------------------- 1 | /* eslint no-await-in-loop: 0 */ 2 | 3 | 'use strict'; 4 | 5 | const config = require('wild-config'); 6 | const yaml = require('js-yaml'); 7 | const fs = require('fs'); 8 | const log = require('npmlog'); 9 | const pathlib = require('path'); 10 | const setupIndexes = yaml.safeLoad(fs.readFileSync(pathlib.join(__dirname, '..', 'setup', 'indexes.yaml'), 'utf8')); 11 | const mongodb = require('mongodb'); 12 | const Redis = require('ioredis'); 13 | 14 | const MongoClient = mongodb.MongoClient; 15 | 16 | module.exports.redis = new Redis(config.dbs.redis); 17 | module.exports.client = false; 18 | module.exports.amqpConnection = false; 19 | module.exports.amqp = false; 20 | module.exports.gridfs = false; 21 | module.exports.senderDb = false; 22 | 23 | module.exports.connect = async () => { 24 | // Apsplication DB 25 | const client = await MongoClient.connect(config.dbs.mongo, { useNewUrlParser: true }); 26 | const db = (module.exports.client = client.db(config.dbs.database)); 27 | 28 | // DB for minimta 29 | if (config.dbs.sender && config.dbs.sender !== config.dbs.database) { 30 | module.exports.senderDb = client.db(config.dbs.sender); 31 | } else { 32 | module.exports.senderDb = db; 33 | } 34 | 35 | return db; 36 | }; 37 | 38 | module.exports.setupIndexes = async () => { 39 | // setup indexes 40 | for (let i = 0; i < setupIndexes.indexes.length; i++) { 41 | let index = setupIndexes.indexes[i]; 42 | try { 43 | await module.exports.client.collection(index.collection).createIndexes([index.index]); 44 | } catch (err) { 45 | log.error(process.pid + '/Mongo', 'Failed creating index %s %s. %s', i, JSON.stringify(index.collection + '.' + index.index.name), err.message); 46 | } 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /lib/initialize.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('util'); 4 | const dns = require('dns'); 5 | const ipify = require('ipify'); 6 | const log = require('npmlog'); 7 | const os = require('os'); 8 | const db = require('./db'); 9 | const settingsModel = require('../models/settings'); 10 | const tools = require('./tools'); 11 | 12 | module.exports = async () => { 13 | // ensure indexes 14 | await db.setupIndexes(); 15 | 16 | // ensure DKIM settings 17 | let dkimData = await settingsModel.get('app_dkim'); 18 | if (!dkimData) { 19 | let dkimData = await tools.generateDkim(); 20 | await settingsModel.set('app_dkim', dkimData); 21 | } 22 | 23 | let hostname = await settingsModel.get('global_site_hostname'); 24 | if (!hostname) { 25 | let addr; 26 | try { 27 | addr = await ipify(); 28 | let hostnames = await utils.promisify(dns.reverse)(addr); 29 | if (hostnames && hostnames.length) { 30 | hostname = hostnames[0]; 31 | } 32 | } catch (err) { 33 | log.error('Ipify', 'error=%s', err.message); 34 | addr = '127.0.0.1'; 35 | hostname = 'localhost'; 36 | } 37 | hostname = hostname || os.hostname() || addr; 38 | await settingsModel.set('global_site_hostname', hostname); 39 | } 40 | 41 | try { 42 | // clear running session info on startup 43 | await db.redis.del('sess'); 44 | } catch (err) { 45 | // ignore 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /lib/timezones.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const tz = require('./tz.json'); 4 | 5 | const timezones = []; 6 | Object.keys(tz.zones).forEach(zoneName => { 7 | timezones.push(zoneName); 8 | }); 9 | 10 | module.exports.timezones = timezones.sort((a, b) => a.localeCompare(b)); 11 | -------------------------------------------------------------------------------- /public/ace/ext-error_marker.js: -------------------------------------------------------------------------------- 1 | ; 2 | (function() { 3 | ace.require(["ace/ext/error_marker"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/ext-linking.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/linking",["require","exports","module","ace/editor","ace/config"],function(e,t,n){function i(e){var n=e.editor,r=e.getAccelKey();if(r){var n=e.editor,i=e.getDocumentPosition(),s=n.session,o=s.getTokenAt(i.row,i.column);t.previousLinkingHover&&t.previousLinkingHover!=o&&n._emit("linkHoverOut"),n._emit("linkHover",{position:i,token:o}),t.previousLinkingHover=o}else t.previousLinkingHover&&(n._emit("linkHoverOut"),t.previousLinkingHover=!1)}function s(e){var t=e.getAccelKey(),n=e.getButton();if(n==0&&t){var r=e.editor,i=e.getDocumentPosition(),s=r.session,o=s.getTokenAt(i.row,i.column);r._emit("linkClick",{position:i,token:o})}}var r=e("ace/editor").Editor;e("../config").defineOptions(r.prototype,"editor",{enableLinking:{set:function(e){e?(this.on("click",s),this.on("mousemove",i)):(this.off("click",s),this.off("mousemove",i))},value:!1}}),t.previousLinkingHover=!1}); 2 | (function() { 3 | ace.require(["ace/ext/linking"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/ext-spellcheck.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/spellcheck",["require","exports","module","ace/lib/event","ace/editor","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event");t.contextMenuHandler=function(e){var t=e.target,n=t.textInput.getElement();if(!t.selection.isEmpty())return;var i=t.getCursorPosition(),s=t.session.getWordRange(i.row,i.column),o=t.session.getTextRange(s);t.session.tokenRe.lastIndex=0;if(!t.session.tokenRe.test(o))return;var u="\x01\x01",a=o+" "+u;n.value=a,n.setSelectionRange(o.length,o.length+1),n.setSelectionRange(0,0),n.setSelectionRange(0,o.length);var f=!1;r.addListener(n,"keydown",function l(){r.removeListener(n,"keydown",l),f=!0}),t.textInput.setInputHandler(function(e){console.log(e,a,n.selectionStart,n.selectionEnd);if(e==a)return"";if(e.lastIndexOf(a,0)===0)return e.slice(a.length);if(e.substr(n.selectionEnd)==a)return e.slice(0,-a.length);if(e.slice(-2)==u){var r=e.slice(0,-2);if(r.slice(-1)==" ")return f?r.substring(0,n.selectionEnd):(r=r.slice(0,-1),t.session.replace(s,r),"")}return e})};var i=e("../editor").Editor;e("../config").defineOptions(i.prototype,"editor",{spellcheck:{set:function(e){var n=this.textInput.getElement();n.spellcheck=!!e,e?this.on("nativecontextmenu",t.contextMenuHandler):this.removeListener("nativecontextmenu",t.contextMenuHandler)},value:!0}})}); 2 | (function() { 3 | ace.require(["ace/ext/spellcheck"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/ext-statusbar.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/statusbar",["require","exports","module","ace/lib/dom","ace/lib/lang"],function(e,t,n){"use strict";var r=e("ace/lib/dom"),i=e("ace/lib/lang"),s=function(e,t){this.element=r.createElement("div"),this.element.className="ace_status-indicator",this.element.style.cssText="display: inline-block;",t.appendChild(this.element);var n=i.delayedCall(function(){this.updateStatus(e)}.bind(this)).schedule.bind(null,100);e.on("changeStatus",n),e.on("changeSelection",n),e.on("keyboardActivity",n)};(function(){this.updateStatus=function(e){function n(e,n){e&&t.push(e,n||"|")}var t=[];n(e.keyBinding.getStatusText(e)),e.commands.recording&&n("REC");var r=e.selection,i=r.lead;if(!r.isEmpty()){var s=e.getSelectionRange();n("("+(s.end.row-s.start.row)+":"+(s.end.column-s.start.column)+")"," ")}n(i.row+":"+i.column," "),r.rangeCount&&n("["+r.rangeCount+"]"," "),t.pop(),this.element.textContent=t.join("")}}).call(s.prototype),t.StatusBar=s}); 2 | (function() { 3 | ace.require(["ace/ext/statusbar"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/ext-themelist.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/ext/themelist",["require","exports","module","ace/lib/fixoldbrowsers"],function(e,t,n){"use strict";e("ace/lib/fixoldbrowsers");var r=[["Chrome"],["Clouds"],["Crimson Editor"],["Dawn"],["Dreamweaver"],["Eclipse"],["GitHub"],["IPlastic"],["Solarized Light"],["TextMate"],["Tomorrow"],["XCode"],["Kuroir"],["KatzenMilch"],["SQL Server","sqlserver","light"],["Ambiance","ambiance","dark"],["Chaos","chaos","dark"],["Clouds Midnight","clouds_midnight","dark"],["Dracula","","dark"],["Cobalt","cobalt","dark"],["Gruvbox","gruvbox","dark"],["Green on Black","gob","dark"],["idle Fingers","idle_fingers","dark"],["krTheme","kr_theme","dark"],["Merbivore","merbivore","dark"],["Merbivore Soft","merbivore_soft","dark"],["Mono Industrial","mono_industrial","dark"],["Monokai","monokai","dark"],["Pastel on dark","pastel_on_dark","dark"],["Solarized Dark","solarized_dark","dark"],["Terminal","terminal","dark"],["Tomorrow Night","tomorrow_night","dark"],["Tomorrow Night Blue","tomorrow_night_blue","dark"],["Tomorrow Night Bright","tomorrow_night_bright","dark"],["Tomorrow Night 80s","tomorrow_night_eighties","dark"],["Twilight","twilight","dark"],["Vibrant Ink","vibrant_ink","dark"]];t.themesByName={},t.themes=r.map(function(e){var n=e[1]||e[0].replace(/ /g,"_").toLowerCase(),r={caption:e[0],theme:"ace/theme/"+n,isDark:e[2]=="dark",name:n};return t.themesByName[n]=r,r})}); 2 | (function() { 3 | ace.require(["ace/ext/themelist"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-ada.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/ada_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="abort|else|new|return|abs|elsif|not|reverse|abstract|end|null|accept|entry|select|access|exception|of|separate|aliased|exit|or|some|all|others|subtype|and|for|out|synchronized|array|function|overriding|at|tagged|generic|package|task|begin|goto|pragma|terminate|body|private|then|if|procedure|type|case|in|protected|constant|interface|until||is|raise|use|declare|range|delay|limited|record|when|delta|loop|rem|while|digits|renames|with|do|mod|requeue|xor",t="true|false|null",n="count|min|max|avg|sum|rank|now|coalesce|main",r=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.AdaHighlightRules=s}),ace.define("ace/mode/ada",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/ada_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./ada_highlight_rules").AdaHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.$id="ace/mode/ada"}.call(o.prototype),t.Mode=o}); 2 | (function() { 3 | ace.require(["ace/mode/ada"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-csp.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/csp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=this.createKeywordMapper({"constant.language":"child-src|connect-src|default-src|font-src|frame-src|img-src|manifest-src|media-src|object-src|script-src|style-src|worker-src|base-uri|plugin-types|sandbox|disown-opener|form-action|frame-ancestors|report-uri|report-to|upgrade-insecure-requests|block-all-mixed-content|require-sri-for|reflected-xss|referrer|policy-uri",variable:"'none'|'self'|'unsafe-inline'|'unsafe-eval'|'strict-dynamic'|'unsafe-hashed-attributes'"},"identifier",!0);this.$rules={start:[{token:"string.link",regex:/https?:[^;\s]*/},{token:"operator.punctuation",regex:/;/},{token:e,regex:/[^\s;]+/}]}};r.inherits(s,i),t.CspHighlightRules=s}),ace.define("ace/mode/csp",["require","exports","module","ace/mode/text","ace/mode/csp_highlight_rules","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./text").Mode,i=e("./csp_highlight_rules").CspHighlightRules,s=e("../lib/oop"),o=function(){this.HighlightRules=i};s.inherits(o,r),function(){this.$id="ace/mode/csp"}.call(o.prototype),t.Mode=o}); 2 | (function() { 3 | ace.require(["ace/mode/csp"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-gcode.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/gcode_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="IF|DO|WHILE|ENDWHILE|CALL|ENDIF|SUB|ENDSUB|GOTO|REPEAT|ENDREPEAT|CALL",t="PI",n="ATAN|ABS|ACOS|ASIN|SIN|COS|EXP|FIX|FUP|ROUND|LN|TAN",r=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t},"identifier",!0);this.$rules={start:[{token:"comment",regex:"\\(.*\\)"},{token:"comment",regex:"([N])([0-9]+)"},{token:"string",regex:"([G])([0-9]+\\.?[0-9]?)"},{token:"string",regex:"([M])([0-9]+\\.?[0-9]?)"},{token:"constant.numeric",regex:"([-+]?([0-9]*\\.?[0-9]+\\.?))|(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)"},{token:r,regex:"[A-Z]"},{token:"keyword.operator",regex:"EQ|LT|GT|NE|GE|LE|OR|XOR"},{token:"paren.lparen",regex:"[\\[]"},{token:"paren.rparen",regex:"[\\]]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.GcodeHighlightRules=s}),ace.define("ace/mode/gcode",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/gcode_highlight_rules","ace/range"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./gcode_highlight_rules").GcodeHighlightRules,o=e("../range").Range,u=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.$id="ace/mode/gcode"}.call(u.prototype),t.Mode=u}); 2 | (function() { 3 | ace.require(["ace/mode/gcode"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-gitignore.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/gitignore_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:/^\s*#.*$/},{token:"keyword",regex:/^\s*!.*$/}]},this.normalizeRules()};s.metaData={fileTypes:["gitignore"],name:"Gitignore"},r.inherits(s,i),t.GitignoreHighlightRules=s}),ace.define("ace/mode/gitignore",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/gitignore_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./gitignore_highlight_rules").GitignoreHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="#",this.$id="ace/mode/gitignore"}.call(o.prototype),t.Mode=o}); 2 | (function() { 3 | ace.require(["ace/mode/gitignore"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-lisp.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/lisp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="case|do|let|loop|if|else|when",t="eq|neq|and|or",n="null|nil",r="cons|car|cdr|cond|lambda|format|setq|setf|quote|eval|append|list|listp|memberp|t|load|progn",i=this.createKeywordMapper({"keyword.control":e,"keyword.operator":t,"constant.language":n,"support.function":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:";.*$"},{token:["storage.type.function-type.lisp","text","entity.name.function.lisp"],regex:"(?:\\b(?:(defun|defmethod|defmacro))\\b)(\\s+)((?:\\w|\\-|\\!|\\?)*)"},{token:["punctuation.definition.constant.character.lisp","constant.character.lisp"],regex:"(#)((?:\\w|[\\\\+-=<>'\"&#])+)"},{token:["punctuation.definition.variable.lisp","variable.other.global.lisp","punctuation.definition.variable.lisp"],regex:"(\\*)(\\S*)(\\*)"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+(?:L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?(?:L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"string",regex:'"(?=.)',next:"qqstring"}],qqstring:[{token:"constant.character.escape.lisp",regex:"\\\\."},{token:"string",regex:'[^"\\\\]+'},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"}]}};r.inherits(s,i),t.LispHighlightRules=s}),ace.define("ace/mode/lisp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/lisp_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./lisp_highlight_rules").LispHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart=";",this.$id="ace/mode/lisp"}.call(o.prototype),t.Mode=o}); 2 | (function() { 3 | ace.require(["ace/mode/lisp"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-lucene.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/lucene_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=function(){this.$rules={start:[{token:"constant.character.negation",regex:"[\\-]"},{token:"constant.character.interro",regex:"[\\?]"},{token:"constant.character.asterisk",regex:"[\\*]"},{token:"constant.character.proximity",regex:"~[0-9]+\\b"},{token:"keyword.operator",regex:"(?:AND|OR|NOT)\\b"},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"keyword",regex:"[\\S]+:"},{token:"string",regex:'".*?"'},{token:"text",regex:"\\s+"}]}};r.inherits(o,s),t.LuceneHighlightRules=o}),ace.define("ace/mode/lucene",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/lucene_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./lucene_highlight_rules").LuceneHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.$id="ace/mode/lucene"}.call(o.prototype),t.Mode=o}); 2 | (function() { 3 | ace.require(["ace/mode/lucene"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-plain_text.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/plain_text",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./text_highlight_rules").TextHighlightRules,o=e("./behaviour").Behaviour,u=function(){this.HighlightRules=s,this.$behaviour=new o};r.inherits(u,i),function(){this.type="text",this.getNextLineIndent=function(e,t,n){return""},this.$id="ace/mode/plain_text"}.call(u.prototype),t.Mode=u}); 2 | (function() { 3 | ace.require(["ace/mode/plain_text"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-properties.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/properties_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=/\\u[0-9a-fA-F]{4}|\\/;this.$rules={start:[{token:"comment",regex:/[!#].*$/},{token:"keyword",regex:/[=:]$/},{token:"keyword",regex:/[=:]/,next:"value"},{token:"constant.language.escape",regex:e},{defaultToken:"variable"}],value:[{regex:/\\$/,token:"string",next:"value"},{regex:/$/,token:"string",next:"start"},{token:"constant.language.escape",regex:e},{defaultToken:"string"}]}};r.inherits(s,i),t.PropertiesHighlightRules=s}),ace.define("ace/mode/properties",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/properties_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./properties_highlight_rules").PropertiesHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.$id="ace/mode/properties"}.call(o.prototype),t.Mode=o}); 2 | (function() { 3 | ace.require(["ace/mode/properties"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-sql.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|when|else|end|type|left|right|join|on|outer|desc|asc|union|create|table|primary|key|if|foreign|not|references|default|null|inner|cross|natural|database|drop|grant",t="true|false",n="avg|count|first|last|max|min|sum|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl",r="int|numeric|decimal|date|varchar|char|bigint|float|double|bit|binary|text|set|timestamp|money|real|number|integer",i=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t,"storage.type":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"comment",start:"/\\*",end:"\\*/"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"string",regex:"`.*?`"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]},this.normalizeRules()};r.inherits(s,i),t.SqlHighlightRules=s}),ace.define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sql_highlight_rules").SqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.$id="ace/mode/sql"}.call(o.prototype),t.Mode=o}); 2 | (function() { 3 | ace.require(["ace/mode/sql"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/mode-text.js: -------------------------------------------------------------------------------- 1 | ; 2 | (function() { 3 | ace.require(["ace/mode/text"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/abap.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/abap",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="abap"}); 2 | (function() { 3 | ace.require(["ace/snippets/abap"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/abc.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/abc",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='\nsnippet zupfnoter.print\n %%%%hn.print {"startpos": ${1:pos_y}, "t":"${2:title}", "v":[${3:voices}], "s":[[${4:syncvoices}1,2]], "f":[${5:flowlines}], "sf":[${6:subflowlines}], "j":[${7:jumplines}]}\n\nsnippet zupfnoter.note\n %%%%hn.note {"pos": [${1:pos_x},${2:pos_y}], "text": "${3:text}", "style": "${4:style}"}\n\nsnippet zupfnoter.annotation\n %%%%hn.annotation {"id": "${1:id}", "pos": [${2:pos}], "text": "${3:text}"}\n\nsnippet zupfnoter.lyrics\n %%%%hn.lyrics {"pos": [${1:x_pos},${2:y_pos}]}\n\nsnippet zupfnoter.legend\n %%%%hn.legend {"pos": [${1:x_pos},${2:y_pos}]}\n\n\n\nsnippet zupfnoter.target\n "^:${1:target}"\n\nsnippet zupfnoter.goto\n "^@${1:target}@${2:distance}"\n\nsnippet zupfnoter.annotationref\n "^#${1:target}"\n\nsnippet zupfnoter.annotation\n "^!${1:text}@${2:x_offset},${3:y_offset}"\n\n\n',t.scope="abc"}); 2 | (function() { 3 | ace.require(["ace/snippets/abc"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/ada.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/ada",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="ada"}); 2 | (function() { 3 | ace.require(["ace/snippets/ada"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/apache_conf.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/apache_conf",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="apache_conf"}); 2 | (function() { 3 | ace.require(["ace/snippets/apache_conf"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/applescript.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/applescript",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="applescript"}); 2 | (function() { 3 | ace.require(["ace/snippets/applescript"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/asciidoc.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/asciidoc",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="asciidoc"}); 2 | (function() { 3 | ace.require(["ace/snippets/asciidoc"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/asl.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/asl",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="asl"}); 2 | (function() { 3 | ace.require(["ace/snippets/asl"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/assembly_x86.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/assembly_x86",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="assembly_x86"}); 2 | (function() { 3 | ace.require(["ace/snippets/assembly_x86"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/autohotkey.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/autohotkey",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="autohotkey"}); 2 | (function() { 3 | ace.require(["ace/snippets/autohotkey"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/batchfile.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/batchfile",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="batchfile"}); 2 | (function() { 3 | ace.require(["ace/snippets/batchfile"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/bro.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/bro",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/bro"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/c9search.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/c9search",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="c9search"}); 2 | (function() { 3 | ace.require(["ace/snippets/c9search"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/cirru.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/cirru",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="cirru"}); 2 | (function() { 3 | ace.require(["ace/snippets/cirru"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/cobol.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/cobol",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="cobol"}); 2 | (function() { 3 | ace.require(["ace/snippets/cobol"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/coldfusion.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/coldfusion",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="coldfusion"}); 2 | (function() { 3 | ace.require(["ace/snippets/coldfusion"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/csharp.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/csharp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="csharp"}); 2 | (function() { 3 | ace.require(["ace/snippets/csharp"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/csound_document.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/csound_document",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# \nsnippet synth\n \n \n ${1}\n \n \n e\n \n \n",t.scope="csound_document"}); 2 | (function() { 3 | ace.require(["ace/snippets/csound_document"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/csound_orchestra.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/csound_orchestra",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# else\nsnippet else\n else\n ${1:/* statements */}\n# elseif\nsnippet elseif\n elseif ${1:/* condition */} then\n ${2:/* statements */}\n# if\nsnippet if\n if ${1:/* condition */} then\n ${2:/* statements */}\n endif\n# instrument block\nsnippet instr\n instr ${1:name}\n ${2:/* statements */}\n endin\n# i-time while loop\nsnippet iwhile\n i${1:Index} = ${2:0}\n while i${1:Index} < ${3:/* count */} do\n ${4:/* statements */}\n i${1:Index} += 1\n od\n# k-rate while loop\nsnippet kwhile\n k${1:Index} = ${2:0}\n while k${1:Index} < ${3:/* count */} do\n ${4:/* statements */}\n k${1:Index} += 1\n od\n# opcode\nsnippet opcode\n opcode ${1:name}, ${2:/* output types */ 0}, ${3:/* input types */ 0}\n ${4:/* statements */}\n endop\n# until loop\nsnippet until\n until ${1:/* condition */} do\n ${2:/* statements */}\n od\n# while loop\nsnippet while\n while ${1:/* condition */} do\n ${2:/* statements */}\n od\n",t.scope="csound_orchestra"}); 2 | (function() { 3 | ace.require(["ace/snippets/csound_orchestra"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/csound_score.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/csound_score",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="csound_score"}); 2 | (function() { 3 | ace.require(["ace/snippets/csound_score"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/csp.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/csp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/csp"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/curly.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/curly",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="curly"}); 2 | (function() { 3 | ace.require(["ace/snippets/curly"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/d.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/d",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="d"}); 2 | (function() { 3 | ace.require(["ace/snippets/d"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/dart.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/dart",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet lib\n library ${1};\n ${2}\nsnippet im\n import '${1}';\n ${2}\nsnippet pa\n part '${1}';\n ${2}\nsnippet pao\n part of ${1};\n ${2}\nsnippet main\n void main() {\n ${1:/* code */}\n }\nsnippet st\n static ${1}\nsnippet fi\n final ${1}\nsnippet re\n return ${1}\nsnippet br\n break;\nsnippet th\n throw ${1}\nsnippet cl\n class ${1:`Filename(\"\", \"untitled\")`} ${2}\nsnippet imp\n implements ${1}\nsnippet ext\n extends ${1}\nsnippet if\n if (${1:true}) {\n ${2}\n }\nsnippet ife\n if (${1:true}) {\n ${2}\n } else {\n ${3}\n }\nsnippet el\n else\nsnippet sw\n switch (${1}) {\n ${2}\n }\nsnippet cs\n case ${1}:\n ${2}\nsnippet de\n default:\n ${1}\nsnippet for\n for (var ${2:i} = 0, len = ${1:things}.length; $2 < len; ${3:++}$2) {\n ${4:$1[$2]}\n }\nsnippet fore\n for (final ${2:item} in ${1:itemList}) {\n ${3:/* code */}\n }\nsnippet wh\n while (${1:/* condition */}) {\n ${2:/* code */}\n }\nsnippet dowh\n do {\n ${2:/* code */}\n } while (${1:/* condition */});\nsnippet as\n assert(${1:/* condition */});\nsnippet try\n try {\n ${2}\n } catch (${1:Exception e}) {\n }\nsnippet tryf\n try {\n ${2}\n } catch (${1:Exception e}) {\n } finally {\n }\n",t.scope="dart"}); 2 | (function() { 3 | ace.require(["ace/snippets/dart"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/diff.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/diff",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='# DEP-3 (http://dep.debian.net/deps/dep3/) style patch header\nsnippet header DEP-3 style header\n Description: ${1}\n Origin: ${2:vendor|upstream|other}, ${3:url of the original patch}\n Bug: ${4:url in upstream bugtracker}\n Forwarded: ${5:no|not-needed|url}\n Author: ${6:`g:snips_author`}\n Reviewed-by: ${7:name and email}\n Last-Update: ${8:`strftime("%Y-%m-%d")`}\n Applied-Upstream: ${9:upstream version|url|commit}\n\n',t.scope="diff"}); 2 | (function() { 3 | ace.require(["ace/snippets/diff"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/dockerfile.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/dockerfile",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="dockerfile"}); 2 | (function() { 3 | ace.require(["ace/snippets/dockerfile"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/dot.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/dot",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="dot"}); 2 | (function() { 3 | ace.require(["ace/snippets/dot"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/drools.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/drools",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='\nsnippet rule\n rule "${1?:rule_name}"\n when\n ${2:// when...} \n then\n ${3:// then...}\n end\n\nsnippet query\n query ${1?:query_name}\n ${2:// find} \n end\n \nsnippet declare\n declare ${1?:type_name}\n ${2:// attributes} \n end\n\n',t.scope="drools"}); 2 | (function() { 3 | ace.require(["ace/snippets/drools"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/eiffel.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/eiffel",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="eiffel"}); 2 | (function() { 3 | ace.require(["ace/snippets/eiffel"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/ejs.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/ejs",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="ejs"}); 2 | (function() { 3 | ace.require(["ace/snippets/ejs"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/elixir.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/elixir",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/elixir"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/elm.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/elm",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="elm"}); 2 | (function() { 3 | ace.require(["ace/snippets/elm"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/forth.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/forth",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="forth"}); 2 | (function() { 3 | ace.require(["ace/snippets/forth"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/fortran.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/fortran",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="fortran"}); 2 | (function() { 3 | ace.require(["ace/snippets/fortran"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/ftl.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/ftl",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="ftl"}); 2 | (function() { 3 | ace.require(["ace/snippets/ftl"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/gcode.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/gcode",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="gcode"}); 2 | (function() { 3 | ace.require(["ace/snippets/gcode"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/gherkin.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/gherkin",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="gherkin"}); 2 | (function() { 3 | ace.require(["ace/snippets/gherkin"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/gitignore.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/gitignore",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="gitignore"}); 2 | (function() { 3 | ace.require(["ace/snippets/gitignore"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/glsl.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/glsl",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="glsl"}); 2 | (function() { 3 | ace.require(["ace/snippets/glsl"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/gobstones.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/gobstones",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# Procedure\nsnippet proc\n procedure ${1?:name}(${2:argument}) {\n ${3:// body...}\n }\n\n# Function\nsnippet fun\n function ${1?:name}(${2:argument}) {\n return ${3:// body...}\n }\n\n# Repeat\nsnippet rep\n repeat ${1?:times} {\n ${2:// body...}\n }\n\n# For\nsnippet for\n foreach ${1?:e} in ${2?:list} {\n ${3:// body...} \n }\n\n# If\nsnippet if\n if (${1?:condition}) {\n ${3:// body...} \n }\n\n# While\n while (${1?:condition}) {\n ${2:// body...} \n }\n",t.scope="gobstones"}); 2 | (function() { 3 | ace.require(["ace/snippets/gobstones"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/golang.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/golang",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="golang"}); 2 | (function() { 3 | ace.require(["ace/snippets/golang"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/graphqlschema.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/graphqlschema",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# Type Snippet\ntrigger type\nsnippet type\n type ${1:type_name} {\n ${2:type_siblings}\n }\n\n# Input Snippet\ntrigger input\nsnippet input\n input ${1:input_name} {\n ${2:input_siblings}\n }\n\n# Interface Snippet\ntrigger interface\nsnippet interface\n interface ${1:interface_name} {\n ${2:interface_siblings}\n }\n\n# Interface Snippet\ntrigger union\nsnippet union\n union ${1:union_name} = ${2:type} | ${3: type}\n\n# Enum Snippet\ntrigger enum\nsnippet enum\n enum ${1:enum_name} {\n ${2:enum_siblings}\n }\n",t.scope="graphqlschema"}); 2 | (function() { 3 | ace.require(["ace/snippets/graphqlschema"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/groovy.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/groovy",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="groovy"}); 2 | (function() { 3 | ace.require(["ace/snippets/groovy"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/haml.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/haml",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet t\n %table\n %tr\n %th\n ${1:headers}\n %tr\n %td\n ${2:headers}\nsnippet ul\n %ul\n %li\n ${1:item}\n %li\nsnippet =rp\n = render :partial => '${1:partial}'\nsnippet =rpl\n = render :partial => '${1:partial}', :locals => {}\nsnippet =rpc\n = render :partial => '${1:partial}', :collection => @$1\n\n",t.scope="haml"}); 2 | (function() { 3 | ace.require(["ace/snippets/haml"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/handlebars.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/handlebars",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="handlebars"}); 2 | (function() { 3 | ace.require(["ace/snippets/handlebars"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/haskell_cabal.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/haskell_cabal",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="haskell_cabal"}); 2 | (function() { 3 | ace.require(["ace/snippets/haskell_cabal"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/haxe.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/haxe",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="haxe"}); 2 | (function() { 3 | ace.require(["ace/snippets/haxe"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/hjson.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/hjson",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/hjson"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/html_elixir.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/html_elixir",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="html_elixir"}); 2 | (function() { 3 | ace.require(["ace/snippets/html_elixir"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/html_ruby.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/html_ruby",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="html_ruby"}); 2 | (function() { 3 | ace.require(["ace/snippets/html_ruby"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/ini.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/ini",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="ini"}); 2 | (function() { 3 | ace.require(["ace/snippets/ini"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/io.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/io",["require","exports","module"],function(e,t,n){"use strict";t.snippets=[{content:"assertEquals(${1:expected}, ${2:expr})",name:"assertEquals",scope:"io",tabTrigger:"ae"},{content:"${1:${2:newValue} := ${3:Object} }clone do(\n $0\n)",name:"clone do",scope:"io",tabTrigger:"cdo"},{content:'docSlot("${1:slotName}", "${2:documentation}")',name:"docSlot",scope:"io",tabTrigger:"ds"},{content:"(${1:header,}\n ${2:body}\n)$0",keyEquivalent:"@(",name:"Indented Bracketed Line",scope:"io",tabTrigger:"("},{content:"\n $0\n",keyEquivalent:"\r",name:"Special: Return Inside Empty Parenthesis",scope:"io meta.empty-parenthesis.io, io meta.comma-parenthesis.io"},{content:"${1:methodName} := method(${2:args,}\n $0\n)",name:"method",scope:"io",tabTrigger:"m"},{content:'newSlot("${1:slotName}", ${2:defaultValue}, "${3:docString}")$0',name:"newSlot",scope:"io",tabTrigger:"ns"},{content:"${1:name} := Object clone do(\n $0\n)",name:"Object clone do",scope:"io",tabTrigger:"ocdo"},{content:"test${1:SomeFeature} := method(\n $0\n)",name:"testMethod",scope:"io",tabTrigger:"ts"},{content:"${1:Something}Test := ${2:UnitTest} clone do(\n $0\n)",name:"UnitTest",scope:"io",tabTrigger:"ut"}],t.scope="io"}); 2 | (function() { 3 | ace.require(["ace/snippets/io"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/jack.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/jack",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="jack"}); 2 | (function() { 3 | ace.require(["ace/snippets/jack"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/jade.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/jade",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="jade"}); 2 | (function() { 3 | ace.require(["ace/snippets/jade"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/json.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/json",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="json"}); 2 | (function() { 3 | ace.require(["ace/snippets/json"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/jsoniq.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/jsoniq",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet for\n for $${1:item} in ${2:expr}\nsnippet return\n return ${1:expr}\nsnippet import\n import module namespace ${1:ns} = "${2:http://www.example.com/}";\nsnippet some\n some $${1:varname} in ${2:expr} satisfies ${3:expr}\nsnippet every\n every $${1:varname} in ${2:expr} satisfies ${3:expr}\nsnippet if\n if(${1:true}) then ${2:expr} else ${3:true}\nsnippet switch\n switch(${1:"foo"})\n case ${2:"foo"}\n return ${3:true}\n default return ${4:false}\nsnippet try\n try { ${1:expr} } catch ${2:*} { ${3:expr} }\nsnippet tumbling\n for tumbling window $${1:varname} in ${2:expr}\n start at $${3:start} when ${4:expr}\n end at $${5:end} when ${6:expr}\n return ${7:expr}\nsnippet sliding\n for sliding window $${1:varname} in ${2:expr}\n start at $${3:start} when ${4:expr}\n end at $${5:end} when ${6:expr}\n return ${7:expr}\nsnippet let\n let $${1:varname} := ${2:expr}\nsnippet group\n group by $${1:varname} := ${2:expr}\nsnippet order\n order by ${1:expr} ${2:descending}\nsnippet stable\n stable order by ${1:expr}\nsnippet count\n count $${1:varname}\nsnippet ordered\n ordered { ${1:expr} }\nsnippet unordered\n unordered { ${1:expr} }\nsnippet treat \n treat as ${1:expr}\nsnippet castable\n castable as ${1:atomicType}\nsnippet cast\n cast as ${1:atomicType}\nsnippet typeswitch\n typeswitch(${1:expr})\n case ${2:type} return ${3:expr}\n default return ${4:expr}\nsnippet var\n declare variable $${1:varname} := ${2:expr};\nsnippet fn\n declare function ${1:ns}:${2:name}(){\n ${3:expr}\n };\nsnippet module\n module namespace ${1:ns} = "${2:http://www.example.com}";\n',t.scope="jsoniq"}); 2 | (function() { 3 | ace.require(["ace/snippets/jsoniq"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/jssm.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/jssm",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/jssm"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/jsx.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/jsx",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="jsx"}); 2 | (function() { 3 | ace.require(["ace/snippets/jsx"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/julia.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/julia",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="julia"}); 2 | (function() { 3 | ace.require(["ace/snippets/julia"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/kotlin.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/kotlin",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/kotlin"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/latex.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/latex",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="latex"}); 2 | (function() { 3 | ace.require(["ace/snippets/latex"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/less.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/less",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="less"}); 2 | (function() { 3 | ace.require(["ace/snippets/less"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/liquid.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/liquid",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="liquid"}); 2 | (function() { 3 | ace.require(["ace/snippets/liquid"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/lisp.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/lisp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="lisp"}); 2 | (function() { 3 | ace.require(["ace/snippets/lisp"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/livescript.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/livescript",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="livescript"}); 2 | (function() { 3 | ace.require(["ace/snippets/livescript"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/logiql.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/logiql",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="logiql"}); 2 | (function() { 3 | ace.require(["ace/snippets/logiql"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/lua.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/lua",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet #!\n #!/usr/bin/env lua\n $1\nsnippet local\n local ${1:x} = ${2:1}\nsnippet fun\n function ${1:fname}(${2:...})\n ${3:-- body}\n end\nsnippet for\n for ${1:i}=${2:1},${3:10} do\n ${4:print(i)}\n end\nsnippet forp\n for ${1:i},${2:v} in pairs(${3:table_name}) do\n ${4:-- body}\n end\nsnippet fori\n for ${1:i},${2:v} in ipairs(${3:table_name}) do\n ${4:-- body}\n end\n",t.scope="lua"}); 2 | (function() { 3 | ace.require(["ace/snippets/lua"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/luapage.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/luapage",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="luapage"}); 2 | (function() { 3 | ace.require(["ace/snippets/luapage"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/lucene.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/lucene",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="lucene"}); 2 | (function() { 3 | ace.require(["ace/snippets/lucene"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/makefile.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/makefile",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet ifeq\n ifeq (${1:cond0},${2:cond1})\n ${3:code}\n endif\n",t.scope="makefile"}); 2 | (function() { 3 | ace.require(["ace/snippets/makefile"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/mask.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/mask",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="mask"}); 2 | (function() { 3 | ace.require(["ace/snippets/mask"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/matlab.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/matlab",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="matlab"}); 2 | (function() { 3 | ace.require(["ace/snippets/matlab"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/maze.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/maze",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet >\ndescription assignment\nscope maze\n -> ${1}= ${2}\n\nsnippet >\ndescription if\nscope maze\n -> IF ${2:**} THEN %${3:L} ELSE %${4:R}\n",t.scope="maze"}); 2 | (function() { 3 | ace.require(["ace/snippets/maze"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/mel.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/mel",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="mel"}); 2 | (function() { 3 | ace.require(["ace/snippets/mel"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/mixal.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/mixal",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="mixal"}); 2 | (function() { 3 | ace.require(["ace/snippets/mixal"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/mushcode.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/mushcode",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="mushcode"}); 2 | (function() { 3 | ace.require(["ace/snippets/mushcode"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/mysql.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/mysql",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="mysql"}); 2 | (function() { 3 | ace.require(["ace/snippets/mysql"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/nix.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/nix",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="nix"}); 2 | (function() { 3 | ace.require(["ace/snippets/nix"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/nsis.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/nsis",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/nsis"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/objectivec.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/objectivec",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="objectivec"}); 2 | (function() { 3 | ace.require(["ace/snippets/objectivec"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/ocaml.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/ocaml",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="ocaml"}); 2 | (function() { 3 | ace.require(["ace/snippets/ocaml"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/pascal.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/pascal",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="pascal"}); 2 | (function() { 3 | ace.require(["ace/snippets/pascal"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/pgsql.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/pgsql",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="pgsql"}); 2 | (function() { 3 | ace.require(["ace/snippets/pgsql"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/pig.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/pig",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="pig"}); 2 | (function() { 3 | ace.require(["ace/snippets/pig"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/plain_text.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/plain_text",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="plain_text"}); 2 | (function() { 3 | ace.require(["ace/snippets/plain_text"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/powershell.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/powershell",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="powershell"}); 2 | (function() { 3 | ace.require(["ace/snippets/powershell"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/praat.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/praat",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="praat"}); 2 | (function() { 3 | ace.require(["ace/snippets/praat"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/prolog.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/prolog",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="prolog"}); 2 | (function() { 3 | ace.require(["ace/snippets/prolog"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/properties.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/properties",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="properties"}); 2 | (function() { 3 | ace.require(["ace/snippets/properties"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/protobuf.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/protobuf",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="protobuf"}); 2 | (function() { 3 | ace.require(["ace/snippets/protobuf"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/razor.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/razor",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet if\n(${1} == ${2}) {\n ${3}\n}",t.scope="razor"}); 2 | (function() { 3 | ace.require(["ace/snippets/razor"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/rdoc.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/rdoc",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="rdoc"}); 2 | (function() { 3 | ace.require(["ace/snippets/rdoc"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/red.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/red",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=" ",t.scope="red"}); 2 | (function() { 3 | ace.require(["ace/snippets/red"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/redshift.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/redshift",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="redshift"}); 2 | (function() { 3 | ace.require(["ace/snippets/redshift"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/rhtml.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/rhtml",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="rhtml"}); 2 | (function() { 3 | ace.require(["ace/snippets/rhtml"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/rst.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/rst",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# rst\n\nsnippet :\n :${1:field name}: ${2:field body}\nsnippet *\n *${1:Emphasis}*\nsnippet **\n **${1:Strong emphasis}**\nsnippet _\n \\`${1:hyperlink-name}\\`_\n .. _\\`$1\\`: ${2:link-block}\nsnippet =\n ${1:Title}\n =====${2:=}\n ${3}\nsnippet -\n ${1:Title}\n -----${2:-}\n ${3}\nsnippet cont:\n .. contents::\n \n",t.scope="rst"}); 2 | (function() { 3 | ace.require(["ace/snippets/rst"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/rust.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/rust",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="rust"}); 2 | (function() { 3 | ace.require(["ace/snippets/rust"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/sass.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/sass",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="sass"}); 2 | (function() { 3 | ace.require(["ace/snippets/sass"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/scad.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/scad",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="scad"}); 2 | (function() { 3 | ace.require(["ace/snippets/scad"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/scala.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/scala",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="scala"}); 2 | (function() { 3 | ace.require(["ace/snippets/scala"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/scheme.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/scheme",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="scheme"}); 2 | (function() { 3 | ace.require(["ace/snippets/scheme"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/scss.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/scss",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="scss"}); 2 | (function() { 3 | ace.require(["ace/snippets/scss"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/sjs.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/sjs",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="sjs"}); 2 | (function() { 3 | ace.require(["ace/snippets/sjs"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/smarty.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/smarty",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="smarty"}); 2 | (function() { 3 | ace.require(["ace/snippets/smarty"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/snippets.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/snippets",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# snippets for making snippets :)\nsnippet snip\n snippet ${1:trigger}\n ${2}\nsnippet msnip\n snippet ${1:trigger} ${2:description}\n ${3}\nsnippet v\n {VISUAL}\n",t.scope="snippets"}); 2 | (function() { 3 | ace.require(["ace/snippets/snippets"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/soy_template.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/soy_template",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="soy_template"}); 2 | (function() { 3 | ace.require(["ace/snippets/soy_template"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/space.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/space",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="space"}); 2 | (function() { 3 | ace.require(["ace/snippets/space"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/sparql.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/sparql",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/sparql"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/sql.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/sql",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet tbl\n create table ${1:table} (\n ${2:columns}\n );\nsnippet col\n ${1:name} ${2:type} ${3:default ''} ${4:not null}\nsnippet ccol\n ${1:name} varchar2(${2:size}) ${3:default ''} ${4:not null}\nsnippet ncol\n ${1:name} number ${3:default 0} ${4:not null}\nsnippet dcol\n ${1:name} date ${3:default sysdate} ${4:not null}\nsnippet ind\n create index ${3:$1_$2} on ${1:table}(${2:column});\nsnippet uind\n create unique index ${1:name} on ${2:table}(${3:column});\nsnippet tblcom\n comment on table ${1:table} is '${2:comment}';\nsnippet colcom\n comment on column ${1:table}.${2:column} is '${3:comment}';\nsnippet addcol\n alter table ${1:table} add (${2:column} ${3:type});\nsnippet seq\n create sequence ${1:name} start with ${2:1} increment by ${3:1} minvalue ${4:1};\nsnippet s*\n select * from ${1:table}\n",t.scope="sql"}); 2 | (function() { 3 | ace.require(["ace/snippets/sql"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/stylus.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/stylus",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="stylus"}); 2 | (function() { 3 | ace.require(["ace/snippets/stylus"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/svg.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/svg",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="svg"}); 2 | (function() { 3 | ace.require(["ace/snippets/svg"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/swift.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/swift",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="swift"}); 2 | (function() { 3 | ace.require(["ace/snippets/swift"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/tcl.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/tcl",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="# #!/usr/bin/env tclsh\nsnippet #!\n #!/usr/bin/env tclsh\n \n# Process\nsnippet pro\n proc ${1:function_name} {${2:args}} {\n ${3:#body ...}\n }\n#xif\nsnippet xif\n ${1:expr}? ${2:true} : ${3:false}\n# Conditional\nsnippet if\n if {${1}} {\n ${2:# body...}\n }\n# Conditional if..else\nsnippet ife\n if {${1}} {\n ${2:# body...}\n } else {\n ${3:# else...}\n }\n# Conditional if..elsif..else\nsnippet ifee\n if {${1}} {\n ${2:# body...}\n } elseif {${3}} {\n ${4:# elsif...}\n } else {\n ${5:# else...}\n }\n# If catch then\nsnippet ifc\n if { [catch {${1:#do something...}} ${2:err}] } {\n ${3:# handle failure...}\n }\n# Catch\nsnippet catch\n catch {${1}} ${2:err} ${3:options}\n# While Loop\nsnippet wh\n while {${1}} {\n ${2:# body...}\n }\n# For Loop\nsnippet for\n for {set ${2:var} 0} {$$2 < ${1:count}} {${3:incr} $2} {\n ${4:# body...}\n }\n# Foreach Loop\nsnippet fore\n foreach ${1:x} {${2:#list}} {\n ${3:# body...}\n }\n# after ms script...\nsnippet af\n after ${1:ms} ${2:#do something}\n# after cancel id\nsnippet afc\n after cancel ${1:id or script}\n# after idle\nsnippet afi\n after idle ${1:script}\n# after info id\nsnippet afin\n after info ${1:id}\n# Expr\nsnippet exp\n expr {${1:#expression here}}\n# Switch\nsnippet sw\n switch ${1:var} {\n ${3:pattern 1} {\n ${4:#do something}\n }\n default {\n ${2:#do something}\n }\n }\n# Case\nsnippet ca\n ${1:pattern} {\n ${2:#do something}\n }${3}\n# Namespace eval\nsnippet ns\n namespace eval ${1:path} {${2:#script...}}\n# Namespace current\nsnippet nsc\n namespace current\n",t.scope="tcl"}); 2 | (function() { 3 | ace.require(["ace/snippets/tcl"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/text.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/text",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="text"}); 2 | (function() { 3 | ace.require(["ace/snippets/text"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/textile.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/textile",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='# Jekyll post header\nsnippet header\n ---\n title: ${1:title}\n layout: post\n date: ${2:date} ${3:hour:minute:second} -05:00\n ---\n\n# Image\nsnippet img\n !${1:url}(${2:title}):${3:link}!\n\n# Table\nsnippet |\n |${1}|${2}\n\n# Link\nsnippet link\n "${1:link text}":${2:url}\n\n# Acronym\nsnippet (\n (${1:Expand acronym})${2}\n\n# Footnote\nsnippet fn\n [${1:ref number}] ${3}\n\n fn$1. ${2:footnote}\n \n',t.scope="textile"}); 2 | (function() { 3 | ace.require(["ace/snippets/textile"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/toml.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/toml",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="toml"}); 2 | (function() { 3 | ace.require(["ace/snippets/toml"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/tsx.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/tsx",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="tsx"}); 2 | (function() { 3 | ace.require(["ace/snippets/tsx"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/turtle.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/turtle",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope=""}); 2 | (function() { 3 | ace.require(["ace/snippets/turtle"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/twig.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/twig",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="twig"}); 2 | (function() { 3 | ace.require(["ace/snippets/twig"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/typescript.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/typescript",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="typescript"}); 2 | (function() { 3 | ace.require(["ace/snippets/typescript"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/vbscript.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/vbscript",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="vbscript"}); 2 | (function() { 3 | ace.require(["ace/snippets/vbscript"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/velocity.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/velocity",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='# macro\nsnippet #macro\n #macro ( ${1:macroName} ${2:\\$var1, [\\$var2, ...]} )\n ${3:## macro code}\n #end\n# foreach\nsnippet #foreach\n #foreach ( ${1:\\$item} in ${2:\\$collection} )\n ${3:## foreach code}\n #end\n# if\nsnippet #if\n #if ( ${1:true} )\n ${0}\n #end\n# if ... else\nsnippet #ife\n #if ( ${1:true} )\n ${2}\n #else\n ${0}\n #end\n#import\nsnippet #import\n #import ( "${1:path/to/velocity/format}" )\n# set\nsnippet #set\n #set ( $${1:var} = ${0} )\n',t.scope="velocity",t.includeScopes=["html","javascript","css"]}); 2 | (function() { 3 | ace.require(["ace/snippets/velocity"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/verilog.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/verilog",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="verilog"}); 2 | (function() { 3 | ace.require(["ace/snippets/verilog"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/vhdl.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/vhdl",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="vhdl"}); 2 | (function() { 3 | ace.require(["ace/snippets/vhdl"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/wollok.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/wollok",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='##\n## Basic Java packages and import\nsnippet im\n import\nsnippet w.l\n wollok.lang\nsnippet w.i\n wollok.lib\n\n## Class and object\nsnippet cl\n class ${1:`Filename("", "untitled")`} ${2}\nsnippet obj\n object ${1:`Filename("", "untitled")`} ${2:inherits Parent}${3}\nsnippet te\n test ${1:`Filename("", "untitled")`}\n\n##\n## Enhancements\nsnippet inh\n inherits\n\n##\n## Comments\nsnippet /*\n /*\n * ${1}\n */\n\n##\n## Control Statements\nsnippet el\n else\nsnippet if\n if (${1}) ${2}\n\n##\n## Create a Method\nsnippet m\n method ${1:method}(${2}) ${5}\n\n## \n## Tests\nsnippet as\n assert.equals(${1:expected}, ${2:actual})\n\n##\n## Exceptions\nsnippet ca\n catch ${1:e} : (${2:Exception} ) ${3}\nsnippet thr\n throw\nsnippet try\n try {\n ${3}\n } catch ${1:e} : ${2:Exception} {\n }\n\n##\n## Javadocs\nsnippet /**\n /**\n * ${1}\n */\n\n##\n## Print Methods\nsnippet print\n console.println("${1:Message}")\n\n##\n## Setter and Getter Methods\nsnippet set\n method set${1:}(${2:}) {\n $1 = $2\n }\nsnippet get\n method get${1:}() {\n return ${1:};\n }\n\n##\n## Terminate Methods or Loops\nsnippet re\n return',t.scope="wollok"}); 2 | (function() { 3 | ace.require(["ace/snippets/wollok"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/xml.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/xml",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="xml"}); 2 | (function() { 3 | ace.require(["ace/snippets/xml"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/xquery.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/xquery",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet for\n for $${1:item} in ${2:expr}\nsnippet return\n return ${1:expr}\nsnippet import\n import module namespace ${1:ns} = "${2:http://www.example.com/}";\nsnippet some\n some $${1:varname} in ${2:expr} satisfies ${3:expr}\nsnippet every\n every $${1:varname} in ${2:expr} satisfies ${3:expr}\nsnippet if\n if(${1:true}) then ${2:expr} else ${3:true}\nsnippet switch\n switch(${1:"foo"})\n case ${2:"foo"}\n return ${3:true}\n default return ${4:false}\nsnippet try\n try { ${1:expr} } catch ${2:*} { ${3:expr} }\nsnippet tumbling\n for tumbling window $${1:varname} in ${2:expr}\n start at $${3:start} when ${4:expr}\n end at $${5:end} when ${6:expr}\n return ${7:expr}\nsnippet sliding\n for sliding window $${1:varname} in ${2:expr}\n start at $${3:start} when ${4:expr}\n end at $${5:end} when ${6:expr}\n return ${7:expr}\nsnippet let\n let $${1:varname} := ${2:expr}\nsnippet group\n group by $${1:varname} := ${2:expr}\nsnippet order\n order by ${1:expr} ${2:descending}\nsnippet stable\n stable order by ${1:expr}\nsnippet count\n count $${1:varname}\nsnippet ordered\n ordered { ${1:expr} }\nsnippet unordered\n unordered { ${1:expr} }\nsnippet treat \n treat as ${1:expr}\nsnippet castable\n castable as ${1:atomicType}\nsnippet cast\n cast as ${1:atomicType}\nsnippet typeswitch\n typeswitch(${1:expr})\n case ${2:type} return ${3:expr}\n default return ${4:expr}\nsnippet var\n declare variable $${1:varname} := ${2:expr};\nsnippet fn\n declare function ${1:ns}:${2:name}(){\n ${3:expr}\n };\nsnippet module\n module namespace ${1:ns} = "${2:http://www.example.com}";\n',t.scope="xquery"}); 2 | (function() { 3 | ace.require(["ace/snippets/xquery"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/snippets/yaml.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/snippets/yaml",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="",t.scope="yaml"}); 2 | (function() { 3 | ace.require(["ace/snippets/yaml"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/theme-gruvbox.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/gruvbox",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!0,t.cssClass="ace-gruvbox",t.cssText='.ace-gruvbox .ace_gutter-active-line {background-color: #3C3836;}.ace-gruvbox {color: #EBDAB4;background-color: #1D2021;}.ace-gruvbox .ace_invisible {color: #504945;}.ace-gruvbox .ace_marker-layer .ace_selection {background: rgba(179, 101, 57, 0.75)}.ace-gruvbox.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #002240;}.ace-gruvbox .ace_keyword {color: #8ec07c;}.ace-gruvbox .ace_comment {font-style: italic;color: #928375;}.ace-gruvbox .ace-statement {color: red;}.ace-gruvbox .ace_variable {color: #84A598;}.ace-gruvbox .ace_variable.ace_language {color: #D2879B;}.ace-gruvbox .ace_constant {color: #C2859A;}.ace-gruvbox .ace_constant.ace_language {color: #C2859A;}.ace-gruvbox .ace_constant.ace_numeric {color: #C2859A;}.ace-gruvbox .ace_string {color: #B8BA37;}.ace-gruvbox .ace_support {color: #F9BC41;}.ace-gruvbox .ace_support.ace_function {color: #F84B3C;}.ace-gruvbox .ace_storage {color: #8FBF7F;}.ace-gruvbox .ace_keyword.ace_operator {color: #EBDAB4;}.ace-gruvbox .ace_punctuation.ace_operator {color: yellow;}.ace-gruvbox .ace_marker-layer .ace_active-line {background: #3C3836;}.ace-gruvbox .ace_marker-layer .ace_selected-word {border-radius: 4px;border: 8px solid #3f475d;}.ace-gruvbox .ace_print-margin {width: 5px;background: #3C3836;}.ace-gruvbox .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNQUFD4z6Crq/sfAAuYAuYl+7lfAAAAAElFTkSuQmCC") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); 2 | (function() { 3 | ace.require(["ace/theme/gruvbox"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/ace/theme-xcode.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/xcode",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-xcode",t.cssText=".ace-xcode .ace_gutter {background: #e8e8e8;color: #333}.ace-xcode .ace_print-margin {width: 1px;background: #e8e8e8}.ace-xcode {background-color: #FFFFFF;color: #000000}.ace-xcode .ace_cursor {color: #000000}.ace-xcode .ace_marker-layer .ace_selection {background: #B5D5FF}.ace-xcode.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #FFFFFF;}.ace-xcode .ace_marker-layer .ace_step {background: rgb(198, 219, 174)}.ace-xcode .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #BFBFBF}.ace-xcode .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.071)}.ace-xcode .ace_gutter-active-line {background-color: rgba(0, 0, 0, 0.071)}.ace-xcode .ace_marker-layer .ace_selected-word {border: 1px solid #B5D5FF}.ace-xcode .ace_constant.ace_language,.ace-xcode .ace_keyword,.ace-xcode .ace_meta,.ace-xcode .ace_variable.ace_language {color: #C800A4}.ace-xcode .ace_invisible {color: #BFBFBF}.ace-xcode .ace_constant.ace_character,.ace-xcode .ace_constant.ace_other {color: #275A5E}.ace-xcode .ace_constant.ace_numeric {color: #3A00DC}.ace-xcode .ace_entity.ace_other.ace_attribute-name,.ace-xcode .ace_support.ace_constant,.ace-xcode .ace_support.ace_function {color: #450084}.ace-xcode .ace_fold {background-color: #C800A4;border-color: #000000}.ace-xcode .ace_entity.ace_name.ace_tag,.ace-xcode .ace_support.ace_class,.ace-xcode .ace_support.ace_type {color: #790EAD}.ace-xcode .ace_storage {color: #C900A4}.ace-xcode .ace_string {color: #DF0002}.ace-xcode .ace_comment {color: #008E00}.ace-xcode .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); 2 | (function() { 3 | ace.require(["ace/theme/xcode"], function(m) { 4 | if (typeof module == "object" && typeof exports == "object" && module) { 5 | module.exports = m; 6 | } 7 | }); 8 | })(); 9 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/favicon-96x96.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/fonts/open-iconic.eot -------------------------------------------------------------------------------- /public/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/fonts/open-iconic.otf -------------------------------------------------------------------------------- /public/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /public/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodemailer/mailcast/57095ff7af45f473436879b678a42c21621f15d0/public/fonts/open-iconic.woff -------------------------------------------------------------------------------- /public/quill/examples/bubble.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bubble Theme - Quill Rich Text Editor 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const router = new express.Router(); 5 | const tools = require('../lib/tools'); 6 | const settingsModel = require('../models/settings'); 7 | 8 | /* GET home page. */ 9 | router.get('/', (req, res) => { 10 | res.render('index', { 11 | page: 'home' 12 | }); 13 | }); 14 | 15 | router.get( 16 | '/help', 17 | tools.asyncify(async (req, res) => { 18 | let siteSettings = await settingsModel.get('global_site_*'); 19 | let dkimData = await settingsModel.get('app_dkim'); 20 | 21 | res.render('help', { 22 | page: 'help', 23 | title: 'Help', 24 | dkim: { 25 | name: dkimData.selector + '._domainkey', 26 | value: 'v=DKIM1;t=s;p=' + dkimData.publicKey.replace(/^-.*-$/gm, '').replace(/\s/g, '') 27 | }, 28 | site: siteSettings 29 | }); 30 | }) 31 | ); 32 | 33 | module.exports = router; 34 | -------------------------------------------------------------------------------- /services/minimta.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | let configLocation = false; 6 | process.argv = process.argv.map(arg => { 7 | if (arg.indexOf('--config=') === 0) { 8 | arg = arg.replace(/\/[^/"']+(["'])?$/, '/mta/minimta.toml$1'); 9 | configLocation = arg; 10 | } 11 | return arg; 12 | }); 13 | 14 | if (!configLocation) { 15 | process.argv.push('--config=' + path.join(__dirname, '..', 'config', 'mta', 'minimta.toml')); 16 | } 17 | 18 | // start the app 19 | require('zone-mta'); 20 | -------------------------------------------------------------------------------- /setup/etc/logrotate.d/mailcast: -------------------------------------------------------------------------------- 1 | /var/log/mailcast/mailcast.log { 2 | daily 3 | ifempty 4 | missingok 5 | rotate 30 6 | compress 7 | create 640 syslog adm 8 | su root root 9 | sharedscripts 10 | postrotate 11 | systemctl kill --signal=SIGHUP --kill-who=main rsyslog.service 2>/dev/null || true 12 | endscript 13 | } 14 | -------------------------------------------------------------------------------- /setup/etc/rsyslog.d/25-mailcast.conf: -------------------------------------------------------------------------------- 1 | if ( $programname startswith "mailcast" ) then { 2 | action(type="omfile" file="/var/log/mailcast/mailcast.log") 3 | stop 4 | } 5 | -------------------------------------------------------------------------------- /setup/etc/systemd/system/mailcast.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Mailcast Mailing List Manager 3 | Conflicts=sendmail.service exim.service postfix.service 4 | After=mongod.service redis.service 5 | 6 | [Service] 7 | Environment="NODE_ENV=production" 8 | WorkingDirectory=APP_ROOT 9 | ExecStart=NODE_PATH server.js --config="/etc/mailcast/mailcast.toml" 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | Type=simple 12 | Restart=always 13 | SyslogIdentifier=mailcast 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /setup/etc/tmpfiles.d/mailcast.conf: -------------------------------------------------------------------------------- 1 | d /var/log/mailcast 0750 syslog adm 2 | d /etc/mailcast 0750 mailcast mailcast 3 | -------------------------------------------------------------------------------- /sources/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": 0, 4 | "prefer-arrow-callback": 0, 5 | "object-shorthand": 0, 6 | "prefer-spread": 0 7 | }, 8 | parserOptions: { 9 | ecmaVersion: 6, 10 | sourceType: 'module' 11 | }, 12 | env: { 13 | browser: true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sources/default-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{SUBJECT}} 9 | 10 | 28 | 29 | 30 | 31 | 32 |
33 | {{{CONTENTS}}} 34 |
35 | 36 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /sources/index.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | // main application file, loads all required js code which is then bundled into a single file 3 | 4 | import $ from 'jquery'; 5 | window.$ = window.jQuery = $; 6 | 7 | // required code for bootstrap extra features (modals, tabs etc) 8 | import 'bootstrap'; 9 | import 'event-source-polyfill'; 10 | 11 | import './js/mailcast.js'; 12 | import './js/actions.js'; 13 | import './js/message.js'; 14 | -------------------------------------------------------------------------------- /sources/js/mailcast.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import 'moment-timezone'; 3 | 4 | let zoneFields; 5 | let currentTimezone = moment.tz.guess(); 6 | 7 | // there should be an 8 | let localeElm = document.getElementById('mm-user-locale'); 9 | if (localeElm && localeElm.value) { 10 | moment.locale(localeElm.value); 11 | } 12 | 13 | // DETECT TIMEZONE 14 | 15 | // update dropdowns 16 | zoneFields = document.querySelectorAll('select.mm-tz-detect option[value="' + currentTimezone + '"]'); 17 | for (let i = 0, len = zoneFields.length; i < len; i++) { 18 | zoneFields[i].selected = true; 19 | } 20 | 21 | // update text/hidden fields 22 | zoneFields = document.querySelectorAll('input.mm-tz-detect'); 23 | for (let i = 0, len = zoneFields.length; i < len; i++) { 24 | zoneFields[i].value = currentTimezone; 25 | } 26 | 27 | // UPDATE DATE VALUES 28 | 29 | // 30 | // 31 | function updateRelativeDate() { 32 | let dateTimeElements = document.querySelectorAll('time.mm-relative-time'); 33 | for (let i = 0, len = dateTimeElements.length; i < len; i++) { 34 | let skipSuffix = !['true', 'yes', 'y', '1'].includes( 35 | (dateTimeElements[i].dataset.mmSuffix || '') 36 | .toString() 37 | .trim() 38 | .toLowerCase() 39 | ); 40 | dateTimeElements[i].textContent = moment(dateTimeElements[i].dateTime).fromNow(skipSuffix); 41 | } 42 | } 43 | 44 | setInterval(updateRelativeDate, 20 * 1000); 45 | updateRelativeDate(); 46 | 47 | // autosubmit forms 48 | zoneFields = document.querySelectorAll('form.mm-autosubmit'); 49 | for (let i = 0, len = zoneFields.length; i < len; i++) { 50 | zoneFields[i].submit(); 51 | } 52 | 53 | // autosubmit forms 54 | zoneFields = document.querySelectorAll('input.mm-clear'); 55 | for (let i = 0, len = zoneFields.length; i < len; i++) { 56 | zoneFields[i].value = ''; 57 | } 58 | -------------------------------------------------------------------------------- /sources/sass/email.scss: -------------------------------------------------------------------------------- 1 | // style definitions for emails 2 | 3 | h3 { 4 | font-size: 22px; 5 | } 6 | -------------------------------------------------------------------------------- /sources/sass/index.scss: -------------------------------------------------------------------------------- 1 | // Main style file, will be exposed as /css/styles.css 2 | // Run `npm run build` to compile the file 3 | 4 | // import bootstrap from node_modules 5 | //@import '../../node_modules/bootstrap/dist/css/bootstrap'; 6 | 7 | @import './bootstrap-pulse.scss'; 8 | @import './open-iconic-bootstrap.scss'; 9 | 10 | nav img.profile-image { 11 | display: inline-block; 12 | border: 0; 13 | } 14 | 15 | html { 16 | position: relative; 17 | min-height: 100%; 18 | } 19 | body { 20 | margin-bottom: 60px; 21 | } 22 | 23 | .footer { 24 | position: absolute; 25 | bottom: 0; 26 | width: 100%; 27 | height: 60px; 28 | line-height: 60px; 29 | background-color: #f5f5f5; 30 | } 31 | 32 | .grecaptcha-badge { 33 | z-index: 100; 34 | } 35 | 36 | .pgp-input, 37 | .monospace { 38 | font-family: monospace; 39 | } 40 | 41 | .ql-placeholder { 42 | width: 112px; 43 | } 44 | 45 | .hidden-text { 46 | position: absolute; 47 | left: -5000px; 48 | top: -5000px; 49 | width: 100px; 50 | height: 100px; 51 | } 52 | 53 | .mm-hidden { 54 | display: none; 55 | } 56 | -------------------------------------------------------------------------------- /views/account/email-validated.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | h2 Success! 5 | p Your email address was successfully validated 6 | -------------------------------------------------------------------------------- /views/account/join.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | h2 Create an account 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | form(method='post', action='/account/join') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | input(type='hidden', name='locale', value=values.locale) 12 | input(type='hidden', name='tz', value=values.tz, class={'mm-tz-detect': !values.tz}) 13 | input.mm-clear(type='hidden', name='e', value='1') 14 | 15 | .form-group 16 | label(for='name') Your name 17 | input#email.form-control(type='name', name='name', placeholder='Your name', required=true, value=values.name, class={'is-invalid': errors && errors.name}) 18 | if errors && errors.name 19 | .invalid-feedback= errors.name 20 | 21 | .form-group 22 | label(for='email') Email address 23 | input#email.form-control(type='email', name='email', placeholder='Your email', required=true, value=values.email, class={'is-invalid': errors && errors.email}) 24 | small.form-text.text-muted Your email address is also going to be your username 25 | if errors && errors.email 26 | .invalid-feedback= errors.email 27 | 28 | .form-group 29 | label(for='password') Password 30 | input#password.form-control(type='password', name='password', placeholder='Password', required=true, pattern='.{8,}', title='8 characters minimum', class={'is-invalid': errors && errors.password}) 31 | small.form-text.text-muted Minimum 8 characters 32 | if errors && errors.password 33 | .invalid-feedback= errors.password 34 | 35 | .form-group 36 | label(for='password2') Repeat password 37 | input#password.form-control(type='password', name='password2', placeholder='Repeat password', required=true, pattern='.{8,}', title='8 characters minimum', class={'is-invalid': errors && errors.password2}) 38 | if errors && errors.password2 39 | .invalid-feedback= errors.password2 40 | 41 | .form-group 42 | button.btn.btn-primary(type='submit') Create an account 43 | -------------------------------------------------------------------------------- /views/account/login.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | h2 Log in 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | form(method='post', action='/account/login') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | 12 | .form-group 13 | label(for='email') Email address 14 | input#email.form-control(type='email', name='email', placeholder='Your email', required=true, autofocus=true, value=values.email, class={'is-invalid': errors && errors.email}) 15 | if errors && errors.email 16 | .invalid-feedback= errors.email 17 | 18 | .form-group 19 | label(for='password') Password 20 | input#password.form-control(type='password', name='password', placeholder='Password', required=true, class={'is-invalid': errors && errors.password}) 21 | if errors && errors.password 22 | .invalid-feedback= errors.password 23 | 24 | .form-check 25 | input#remember.form-check-input(type='checkbox', name='remember', checked=values.remember, class={'is-invalid': errors && errors.remember}) 26 | label(for='remember') Remember me 27 | if errors && errors.remember 28 | .invalid-feedback= errors.remember 29 | 30 | .form-group 31 | button.btn.btn-primary(type='submit') Log in 32 | = " " 33 | a(href='/account/recover') Forgot password? 34 | -------------------------------------------------------------------------------- /views/account/recover.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | h2 Account recovery 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | form(method='post', action='/account/recover') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | 12 | .form-group 13 | label(for='email') Email address 14 | input#email.form-control(type='email', name='email', placeholder='Your email', required=true, value=values.email, class={'is-invalid': errors && errors.email}) 15 | if errors && errors.email 16 | .invalid-feedback= errors.email 17 | 18 | .form-group 19 | button.btn.btn-primary(type='submit') Send recovery code 20 | -------------------------------------------------------------------------------- /views/account/reset.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | h2 Account recovery 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | p Set a new password for your account 10 | 11 | form(method='post', action='/account/reset') 12 | input(type='hidden', name='_csrf', value=csrf) 13 | input(type='hidden', name='token', value=values.token) 14 | input(type='hidden', name='email', value=values.email) 15 | 16 | .form-group 17 | label(for='email') Email address 18 | input#email.form-control-plaintext(type='text', readonly=true, value=values.email) 19 | 20 | .form-group 21 | label(for='password') New password 22 | input#password.form-control(type='password', name='password', placeholder='Password', required=true, pattern='.{8,}', title='8 characters minimum', class={'is-invalid': errors && errors.password}) 23 | if errors && errors.password 24 | .invalid-feedback= errors.password 25 | 26 | .form-group 27 | label(for='password2') Repeat password 28 | input#password.form-control(type='password', name='password2', placeholder='Repeat password', required=true, pattern='.{8,}', title='8 characters minimum', class={'is-invalid': errors && errors.password2}) 29 | if errors && errors.password2 30 | .invalid-feedback= errors.password2 31 | 32 | .form-group 33 | button.btn.btn-primary(type='submit') Set new password 34 | -------------------------------------------------------------------------------- /views/account/set-password.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | h2 Set a password for your account 5 | 6 | form(method='post', action='/account/set-password') 7 | div.form-group 8 | label(for='password') Set password 9 | input.form-control(type='password', name='password', id='password', placeholder='Set password') 10 | small.form-text.text-muted Minimum 8 characters 11 | if errors && errors.password 12 | .invalid-feedback= errors.password 13 | 14 | div.form-group 15 | label(for='password2') Repeat password 16 | input.form-control(type='password', name='password2', id='password2', placeholder='Repeat password') 17 | if errors && errors.password2 18 | .invalid-feedback= errors.password2 19 | 20 | div.form-group 21 | button.btn.btn-primary(type='submit') Set password 22 | -------------------------------------------------------------------------------- /views/account/settings/password.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/main.pug 2 | 3 | block content 4 | 5 | include settings-header 6 | 7 | form(method='post', action='/account/settings/password') 8 | input(type='hidden', name='_csrf', value=csrf) 9 | 10 | .form-group 11 | label(for='existingPassword') Existing password 12 | input#existingPassword.form-control(type='password', name='existingPassword', placeholder='Existing password', required=true, pattern='.{8,}', title='8 characters minimum', class={'is-invalid': errors && errors.existingPassword}) 13 | if errors && errors.existingPassword 14 | .invalid-feedback= errors.existingPassword 15 | 16 | .form-group 17 | label(for='password') New password 18 | input#password.form-control(type='password', name='password', placeholder='New password', required=true, pattern='.{8,}', title='8 characters minimum', class={'is-invalid': errors && errors.password}) 19 | small.form-text.text-muted Minimum 8 characters 20 | if errors && errors.password 21 | .invalid-feedback= errors.password 22 | 23 | .form-group 24 | label(for='password2') Repeat password 25 | input#password.form-control(type='password', name='password2', placeholder='Repeat password', required=true, pattern='.{8,}', title='8 characters minimum', class={'is-invalid': errors && errors.password2}) 26 | if errors && errors.password2 27 | .invalid-feedback= errors.password2 28 | 29 | .form-group 30 | button.btn.btn-primary(type='submit') Update password 31 | = " " 32 | a.btn.btn-warning(href='/account/settings') Cancel 33 | -------------------------------------------------------------------------------- /views/account/settings/profile.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/main.pug 2 | 3 | block content 4 | 5 | include settings-header 6 | 7 | form(method='post', action='/account/settings/profile') 8 | input(type='hidden', name='_csrf', value=csrf) 9 | 10 | .form-group 11 | label(for='email') Email address 12 | input#email.form-control-plaintext(type='text', readonly=true, value=user.email) 13 | 14 | if !user.emailValidated 15 | div.mt-3.alert.alert-warning Your email address #{user.email} is not yet validated. You can not send campaigns with an unvalidated account. 16 | =" " 17 | a.alert-link.mm-action(href='#', data-mm-event='click', data-mm-action='resendValidation') Resend validation link 18 | 19 | .form-group 20 | label(for='name') Your name 21 | input#name.form-control(type='name', name='name', placeholder='Your name', required=true, value=values.name, class={'is-invalid': errors && errors.name}) 22 | if errors && errors.name 23 | .invalid-feedback= errors.name 24 | 25 | .form-group 26 | label(for='locale') Locale 27 | select#locale.form-control(name='locale', class={'is-invalid': errors && errors.locale}) 28 | for item in locales 29 | option(value=item.code, selected=values.locale == item.code) #{item.language} (#{item.country}) 30 | small.form-text.text-muted Date and number format 31 | if errors && errors.locale 32 | .invalid-feedback= errors.locale 33 | 34 | .form-group 35 | label(for='tz') Timezone 36 | select#tz.form-control(name='tz', class={'is-invalid': errors && errors.locale}, class={'mm-tz-detect': !values.tz}) 37 | for timezone in timezones 38 | option(value=timezone, selected=values.tz == timezone) #{timezone} 39 | small.form-text.text-muted Your timezone 40 | if errors && errors.tz 41 | .invalid-feedback= errors.tz 42 | 43 | .form-group 44 | button.btn.btn-primary(type='submit') Save changes 45 | -------------------------------------------------------------------------------- /views/account/settings/settings-header.pug: -------------------------------------------------------------------------------- 1 | 2 | h2 Settings 3 | 4 | p.lead This is where you can manage your account settings. 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | include settings-menu.pug 10 | 11 | div.clearfix.mt-3 12 | -------------------------------------------------------------------------------- /views/account/settings/settings-menu.pug: -------------------------------------------------------------------------------- 1 | nav.nav.nav-tabs 2 | li.nav-item 3 | a.nav-link(href='/account/settings/', class={'active': settingsPage=='profile'}) Profile 4 | li.nav-item 5 | a.nav-link(href='/account/settings/password', class={'active': settingsPage=='password'}) Password 6 | if user.status === 'admin' 7 | li.nav-item 8 | a.nav-link(href='/account/settings/site', class={'active': settingsPage=='site'}) Site settings 9 | -------------------------------------------------------------------------------- /views/archive/view.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3= listData.name 5 | 6 | p.lead Archived messages is a future feature for #{appname} 7 | -------------------------------------------------------------------------------- /views/edit.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | 5 | if error 6 | +alert('danger', error) 7 | 8 | include message-header 9 | 10 | form(method='post', action='/messages/edit') 11 | input(type='hidden', name='_csrf', value=csrf) 12 | 13 | include ./message-form.pug 14 | 15 | .form-group 16 | button.btn.btn-primary(type="submit") Create draft 17 | = " " 18 | a.btn.btn-warning(href='/lists') Cancel 19 | 20 | block append css 21 | link(href='/quill/quill.snow.css', rel='stylesheet') 22 | 23 | block append scripts 24 | script(src='/quill/quill.min.js') 25 | script(src='/editor.js') -------------------------------------------------------------------------------- /views/emails/README.md: -------------------------------------------------------------------------------- 1 | # email templates 2 | 3 | Email templates are looked for using folder name, for example the contents for 'email-validation' are searched from views/emails/{email-validation}/\*.pug 4 | 5 | ### Template files 6 | 7 | * **html.pug** is the template for email HTML content 8 | * **text.pug** is the template for email plaintext content 9 | * **subject.pug** is the subject line for the message 10 | 11 | Jade/pug is not so cool when using plaintext, you need to start all lines with the pipe symbol 12 | -------------------------------------------------------------------------------- /views/emails/account-recovery/html.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/email.pug 2 | 3 | block content 4 | h3 Hello #{userData.name} 5 | p Someone (hopefully You) has asked to reset your account password. 6 | 7 | p 8 | span You can set a new password for your #{appname} account 9 | =" " 10 | a.action-link(href=recoveryUrl) here 11 | | . 12 | -------------------------------------------------------------------------------- /views/emails/account-recovery/subject.pug: -------------------------------------------------------------------------------- 1 | | #{appname} account recovery for #{userData.name} 2 | -------------------------------------------------------------------------------- /views/emails/account-recovery/text.pug: -------------------------------------------------------------------------------- 1 | //- plaintext message content 2 | 3 | | Hello !{userData.name} 4 | | 5 | | Someone (hopefully You) has asked to reset your account password. 6 | | You can set a new password for your #{appname} account here: !{validationUrl} 7 | | 8 | | !{appname} at (!{appurl}) 9 | -------------------------------------------------------------------------------- /views/emails/email-validation/html.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/email.pug 2 | 3 | block content 4 | h3 Hello #{userData.name} 5 | p Welcome to #{appname}! 6 | 7 | p 8 | span Your email address needs to be validated. Click 9 | =" " 10 | a.action-link(href=validationUrl) here 11 | =" " 12 | span to validate your email address. 13 | -------------------------------------------------------------------------------- /views/emails/email-validation/subject.pug: -------------------------------------------------------------------------------- 1 | | Welcome to #{appname}, #{userData.name} 2 | -------------------------------------------------------------------------------- /views/emails/email-validation/text.pug: -------------------------------------------------------------------------------- 1 | //- plaintext message content 2 | 3 | | Hello !{userData.name} 4 | | 5 | | Welcome to !{appname}! 6 | | Your email address needs to be validated 7 | | Click here to validate your email address: !{validationUrl} 8 | | 9 | | !{appname} at (!{appurl}) 10 | -------------------------------------------------------------------------------- /views/emails/subscriber-change/html.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/email.pug 2 | 3 | block content 4 | h3 Please Confirm E-mail Change 5 | 6 | p 7 | a.action-link(href=validationUrl) Yes, confirm my updated e-mail address 8 | 9 | p If you received this email by mistake, simply delete it. You won't be subscribed if you don't click the confirmation link above. 10 | 11 | p For questions about this list, please contact: 12 | =" " 13 | a(href='mailto:'+listData.email)= listData.email 14 | -------------------------------------------------------------------------------- /views/emails/subscriber-change/subject.pug: -------------------------------------------------------------------------------- 1 | | #{listData.name}: Please Confirm E-mail Change 2 | -------------------------------------------------------------------------------- /views/emails/subscriber-change/text.pug: -------------------------------------------------------------------------------- 1 | //- plaintext message content 2 | 3 | | Please Confirm E-mail Change 4 | | ============================ 5 | | 6 | | Yes, confirm my updated e-mail address: !{validationUrl} 7 | | 8 | | If you received this email by mistake, simply delete it. You won't be subscribed unless you click the confirmation link above. 9 | | 10 | | For questions about this list, please contact: !{listData.email} 11 | | 12 | | !{appname} at (!{appurl}) 13 | -------------------------------------------------------------------------------- /views/emails/subscriber-confirm/html.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/email.pug 2 | 3 | block content 4 | h3 Please Confirm Subscription 5 | 6 | p Thank you for signing up to #{listData.name}. 7 | 8 | p 9 | a.action-link(href=validationUrl) Click here to confirm your subscription 10 | 11 | p If you received this email by mistake, simply delete it. You won't be subscribed if you don't click the confirmation link above. 12 | 13 | p For questions about this list, please contact: 14 | =" " 15 | a(href='mailto:'+listData.email)= listData.email 16 | -------------------------------------------------------------------------------- /views/emails/subscriber-confirm/subject.pug: -------------------------------------------------------------------------------- 1 | | #{listData.name}: Please Confirm Subscription 2 | -------------------------------------------------------------------------------- /views/emails/subscriber-confirm/text.pug: -------------------------------------------------------------------------------- 1 | //- plaintext message content 2 | 3 | | Please Confirm Subscription 4 | | =========================== 5 | | 6 | | Thank you for signing up to !{listData.name}. 7 | | 8 | | Click here to confirm your subscription: !{validationUrl} 9 | | 10 | | If you received this email by mistake, simply delete it. You won't be subscribed unless you click the confirmation link above. 11 | | 12 | | For questions about this list, please contact: !{listData.email} 13 | | 14 | | !{appname} at (!{appurl}) 15 | -------------------------------------------------------------------------------- /views/emails/subscriptions-token/html.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/email.pug 2 | 3 | block content 4 | h3 Subscription listing 5 | 6 | p Click on the link below to view all subscriptions of #{email} at #{appname} 7 | 8 | p 9 | a.action-link(href=subscriptionsUrl) List my subscriptions 10 | 11 | p The given link expires in 24 hours. 12 | -------------------------------------------------------------------------------- /views/emails/subscriptions-token/subject.pug: -------------------------------------------------------------------------------- 1 | | Subscription listing for #{appname} 2 | -------------------------------------------------------------------------------- /views/emails/subscriptions-token/text.pug: -------------------------------------------------------------------------------- 1 | //- plaintext message content 2 | 3 | | Subscription listing 4 | | ==================== 5 | | 6 | | Click on the link below to view all subscriptions of #{email} at !{appname}: !{subscriptionsUrl} 7 | | 8 | | The given link expires in 24 hours. 9 | | 10 | | !{appname} at (!{appurl}) 11 | -------------------------------------------------------------------------------- /views/emails/welcome/html.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/email.pug 2 | 3 | block content 4 | h3 Hello #{userData.name}, 5 | 6 | p Welcome to #{appname}! 7 | 8 | p 9 | span You can log in to your account 10 | =" " 11 | a.action-link(href=loginUrl) by visiting this link 12 | ="." 13 | 14 | p Your password: 15 | = " " 16 | code #{userData.password} 17 | 18 | p You should change this password as soon as possible 19 | -------------------------------------------------------------------------------- /views/emails/welcome/subject.pug: -------------------------------------------------------------------------------- 1 | | Welcome to #{appname}, #{userData.name} 2 | -------------------------------------------------------------------------------- /views/emails/welcome/text.pug: -------------------------------------------------------------------------------- 1 | //- plaintext message content 2 | 3 | | Hello !{userData.name}, 4 | | 5 | | Welcome to !{appname}! 6 | | You can log in to your account here: !{loginUrl} 7 | | Use the following password: !{userData.password} 8 | | 9 | | !{appname} at (!{appurl}) 10 | -------------------------------------------------------------------------------- /views/error.pug: -------------------------------------------------------------------------------- 1 | extends layouts/main.pug 2 | 3 | block content 4 | h2 #{status} Error 5 | p.lead= message 6 | if error 7 | pre= error.stack 8 | -------------------------------------------------------------------------------- /views/help.pug: -------------------------------------------------------------------------------- 1 | extends layouts/main.pug 2 | 3 | block content 4 | h2 #{appname} Help 5 | 6 | hr 7 | 8 | h3 DKIM settings 9 | 10 | p #{appname} signs all messages with the same key, so make sure your sending domains have the same selector and key set 11 | 12 | .form-group 13 | label DKIM Selector 14 | input.form-control.pgp-input(type='text', value=dkim.name, readonly=true) 15 | small.form-text.text-muted Use this value as the subdomain name of your email domain for a DNS TXT record 16 | 17 | .form-group 18 | label DKIM Public Key 19 | input.form-control.pgp-input(type='text', value=dkim.value, readonly=true) 20 | small.form-text.text-muted Use this as the value for the DNS TXT record 21 | -------------------------------------------------------------------------------- /views/index.pug: -------------------------------------------------------------------------------- 1 | extends layouts/subscribers.pug 2 | 3 | block content 4 | h2 #{appname} 5 | 6 | h5 What is this page? 7 | 8 | p You have reached an email newsletter management software running at 9 | = " " 10 | em #{appurl} 11 | |. 12 | 13 | h5 How can I unsubscribe? 14 | 15 | p If you want to unsubscribe from a list managed by #{appname} then find the unsubscribe link from a list message or alternatively, find the unsubscribe link from message headers. For example in Gmail select "Show original" from message menu and in the opened window look for the List-Unsubscribe header. 16 | 17 | p You can also list all your subscriptions and unsubscribe here. 18 | 19 | hr 20 | 21 | h5 List admins 22 | 23 | if user 24 | p 25 | a.btn.btn-primary(href='/lists') Enter 26 | = " or " 27 | a.btn.btn-primary(href='/account/logout') Log out 28 | else 29 | p 30 | if !disableJoin 31 | a.btn.btn-primary(href='/account/join') Create an account 32 | = " or " 33 | a.btn.btn-primary(href='/account/login') Log in 34 | -------------------------------------------------------------------------------- /views/layouts/email.pug: -------------------------------------------------------------------------------- 1 | //- use this template for email HTML content, do not use javascript as it will be stripped from final output. 2 | //- Styles from CSS files are automatically inlined using Juice https://github.com/Automattic/juice 3 | 4 | doctype html 5 | html(lang='en') 6 | head 7 | meta(charset='utf-8') 8 | meta(name='viewport', content='width=device-width, initial-scale=1, shrink-to-fit=no') 9 | link(href='/css/email.css', rel='stylesheet') 10 | 11 | body 12 | .container 13 | block content 14 | //- placeholder that is replaced by child templates 15 | 16 | block footer 17 | #footer 18 | p 19 | a(href=appurl) #{appname} 20 | -------------------------------------------------------------------------------- /views/layouts/main.pug: -------------------------------------------------------------------------------- 1 | - var messages = flash() //- fetch pending alerts 2 | 3 | include ../mixins.pug 4 | 5 | doctype html 6 | html(lang='en') 7 | head 8 | 9 | // Required meta tags 10 | meta(charset='utf-8') 11 | meta(name='viewport', content='width=device-width, initial-scale=1, shrink-to-fit=no') 12 | 13 | if title 14 | title= appname +' | ' + title 15 | else 16 | title= appname 17 | 18 | link(rel="icon", type="image/png", sizes="96x96", href="/favicon-96x96.png") 19 | link(rel="icon", type="image/png", sizes="32x32", href="/favicon-32x32.png") 20 | link(rel="icon", type="image/png", sizes="16x16", href="/favicon-16x16.png") 21 | link(rel="shortcut icon", href="/favicon.ico", type="image/x-icon") 22 | link(rel="icon", href="/favicon.ico", type="image/x-icon") 23 | 24 | block css 25 | link(href='/css/styles.css', rel='stylesheet') 26 | 27 | body 28 | input#mm-user-locale(type='hidden', value=locale) 29 | input#_csrf(type='hidden', value=csrf) 30 | 31 | include ../partials/main-menu.pug 32 | 33 | .container(style='margin-top: 60px;') 34 | 35 | include ../partials/flash-messages.pug 36 | 37 | block content 38 | //- placeholder that is replaced by child templates 39 | 40 | div.mt-3   41 | footer.footer 42 | .container 43 | span.text-muted #{appname} is powered by 44 | = " " 45 | a(href='https://mailcast.app') Mailcast 46 | 47 | div.modal#mm-alertbox 48 | div.modal-dialog 49 | div.modal-content 50 | div.modal-header 51 | h5.modal-title.mm-alertbox-title 52 | button.close(type="button", data-dismiss="modal") 53 | span × 54 | div.modal-body 55 | p.mm-alertbox-body 56 | div.modal-footer 57 | button.btn.btn-secondary.mm-alertbox-btn(type="button", data-dismiss="modal") Close 58 | 59 | block scripts 60 | script(src='/app/bundle.js') 61 | -------------------------------------------------------------------------------- /views/layouts/subscribers.pug: -------------------------------------------------------------------------------- 1 | - var messages = flash() //- fetch pending alerts 2 | 3 | include ../mixins.pug 4 | 5 | doctype html 6 | html(lang='en') 7 | head 8 | 9 | // Required meta tags 10 | meta(charset='utf-8') 11 | meta(name='viewport', content='width=device-width, initial-scale=1, shrink-to-fit=no') 12 | 13 | if title 14 | title= appname +' | ' + title 15 | else 16 | title= appname 17 | 18 | block css 19 | link(href='/css/styles.css', rel='stylesheet') 20 | 21 | body 22 | input#mm-user-locale(type='hidden', value=locale) 23 | 24 | .container(style='margin-top: 60px;') 25 | 26 | include ../partials/flash-messages.pug 27 | 28 | if listData && listData.header 29 | div.mt-3 !{listData.header} 30 | div.mt-3 31 | 32 | block content 33 | //- placeholder that is replaced by child templates 34 | 35 | block footer 36 | div.mt-3   37 | footer.footer 38 | .container 39 | span.text-muted #{appname} is powered by 40 | = " " 41 | a(href='https://mailcast.app') Mailcast 42 | 43 | block scripts 44 | script(src='/app/bundle.js') 45 | 46 | if recaptcha 47 | script(src='https://www.google.com/recaptcha/api.js') 48 | 49 | script. 50 | function onCreateSubmit(token) { 51 | let submitForm = document.querySelector('form.mm-captcha-form'); 52 | if(submitForm){ 53 | submitForm.submit(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /views/lists/add.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | 5 | if error 6 | +alert('danger', error) 7 | 8 | include list-main-header 9 | 10 | form(method='post', action='/lists/add') 11 | input(type='hidden', name='_csrf', value=csrf) 12 | 13 | include ./list-form.pug 14 | 15 | .form-group 16 | label(for='fields') Custom fields 17 | textarea#fields.form-control(name='fields', placeholder='Where did you hear from us?', rows=5, class={'is-invalid': errors && errors.fields})= values.fields 18 | small.form-text.text-muted One field name per line. Every field gets its own textbox in the subscription form. 19 | if errors && errors.fields 20 | .invalid-feedback= errors.fields 21 | 22 | .form-group 23 | button.btn.btn-primary(type="submit") Create a list 24 | = " " 25 | a.btn.btn-warning(href='/lists') Cancel 26 | -------------------------------------------------------------------------------- /views/lists/edit.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | 5 | if error 6 | +alert('danger', error) 7 | 8 | include list-header 9 | 10 | form(method='post', action='/lists/edit') 11 | input(type='hidden', name='_csrf', value=csrf) 12 | input(type='hidden', name='list', value=list) 13 | 14 | include ./list-form.pug 15 | 16 | .float-right 17 | button.btn.btn-danger(type="button", data-toggle="modal", data-target="#mm-delete-confirm") Delete list 18 | 19 | .form-group 20 | button.btn.btn-primary(type="submit") Save changes 21 | = " " 22 | a.btn.btn-warning(href='/lists/view/'+list) Cancel 23 | 24 | form(method='post', action='/lists/delete') 25 | input(type='hidden', name='_csrf', value=csrf) 26 | input(type='hidden', name='list', value=list) 27 | +confirmDelete('mm-delete-confirm', 'Delete list', 'Are you sure you want to delete this list?', 'No, cancel', 'Yes, delete') 28 | -------------------------------------------------------------------------------- /views/lists/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | 5 | include list-main-header 6 | 7 | if !lists.length 8 | p No lists created so far, 9 | = " " 10 | a(href='/lists/add') create one here 11 | |. 12 | 13 | else 14 | table.table.mt-3 15 | thead 16 | tr 17 | th # 18 | th Name 19 | th Subscribers 20 | th   21 | tbody 22 | for listData in lists 23 | tr 24 | th= listData.nr 25 | 26 | td 27 | a(href='/lists/view/'+listData._id) 28 | span.oi.oi-list 29 | = " " + listData.name 30 | 31 | td #{msg('{N, plural, =0 {no subscribers} one {1 subscriber} other {# subscribers}}',{N: listData.subscribers})} 32 | td 33 | a(href='/lists/edit/'+listData._id) Edit 34 | 35 | +paging(curpage, pages, pagingUrl) 36 | -------------------------------------------------------------------------------- /views/lists/list-form.pug: -------------------------------------------------------------------------------- 1 | .form-group 2 | label(for='name') List name 3 | input#name.form-control(type='text', name='name', placeholder='List name', required=true, value=values.name, class={'is-invalid': errors && errors.name}) 4 | if errors && errors.name 5 | .invalid-feedback= errors.name 6 | 7 | .form-group 8 | label(for='description') List description 9 | input#description.form-control(type='text', name='description', placeholder='List description', value=values.description, class={'is-invalid': errors && errors.description}) 10 | small.form-text.text-muted Something that describes this list. 11 | if errors && errors.description 12 | .invalid-feedback= errors.description 13 | 14 | .form-group 15 | label(for='email') From email address 16 | input#email.form-control(type='email', name='email', placeholder='From email', required=true, value=values.email, class={'is-invalid': errors && errors.email}) 17 | small.form-text.text-muted This is the address people will reply to. See Help page for DKIM settings 18 | if errors && errors.email 19 | .invalid-feedback= errors.email 20 | 21 | .form-group 22 | label(for='fromname') From name 23 | input#fromname.form-control(type='text', name='fromname', placeholder='From name', required=true, value=values.fromname, class={'is-invalid': errors && errors.fromname}) 24 | small.form-text.text-muted This is who your emails will come from. 25 | if errors && errors.fromname 26 | .invalid-feedback= errors.fromname 27 | 28 | .form-group 29 | label(for='mmHeader') Header HTML 30 | textarea#mmHeader.form-control(name='header', placeholder='

My Awesome Homepage

', rows=5, class={'is-invalid': errors && errors.header})= values.header 31 | small.form-text.text-muted Optional HTML code to be shown in the header of the subscription form. 32 | if errors && errors.header 33 | .invalid-feedback= errors.header 34 | 35 | div.form-group.form-check 36 | input.form-check-input#pgp(type='checkbox', name='pgp', checked=values.pgp) 37 | label.form-check-label(for="pgp") PGP encryption 38 | small.form-text.text-muted Add PGP public key field for the subscription form. 39 | -------------------------------------------------------------------------------- /views/lists/list-header.pug: -------------------------------------------------------------------------------- 1 | div.float-right 2 | div.dropdown 3 | button.btn.btn-info.dropdown-toggle(type="button", data-toggle="dropdown") List actions 4 | div.dropdown-menu 5 | a.dropdown-item(href='/messages/add?list=' + list) Compose new message 6 | a.dropdown-item(href='/subscribers/subscribe/' + list, target='_blank') Subscription form 7 | a.dropdown-item(href='/lists/subscribers/' + list + '/add') Add subscriber 8 | 9 | h2= listData.name 10 | p.lead= listData.description 11 | 12 | include list-menu.pug 13 | 14 | div.clearfix.mt-3 15 | -------------------------------------------------------------------------------- /views/lists/list-main-header.pug: -------------------------------------------------------------------------------- 1 | div.float-right 2 | a.btn.btn-info(href='/lists/add') Add new list 3 | 4 | h2 Lists 5 | p.lead This is where you can manage your lists of subscribers. 6 | 7 | nav.nav.nav-tabs 8 | li.nav-item 9 | a.nav-link(href='/lists', class={'active': listPage=='list'}) Lists 10 | if listPage=='manage' 11 | li.nav-item 12 | a.nav-link.active(href='#')= title 13 | 14 | div.clearfix.mt-3 15 | -------------------------------------------------------------------------------- /views/lists/list-menu.pug: -------------------------------------------------------------------------------- 1 | nav.nav.nav-tabs 2 | li.nav-item 3 | a.nav-link(href='/lists/view/'+listData._id, class={'active': listPage=='view'}) Subscribers 4 | li.nav-item 5 | a.nav-link(href='/lists/fields/'+listData._id, class={'active': listPage=='fields'}) Form fields 6 | li.nav-item 7 | a.nav-link(href='/lists/edit/'+listData._id, class={'active': listPage=='edit'}) Settings 8 | if listPage=='manage' 9 | li.nav-item 10 | a.nav-link.active(href='#')= title 11 | -------------------------------------------------------------------------------- /views/lists/subscribers/add.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/main.pug 2 | 3 | block content 4 | 5 | if error 6 | +alert('danger', error) 7 | 8 | include ../list-header 9 | 10 | if error 11 | +alert('danger', error) 12 | 13 | form(method='post', action='/lists/subscribers/'+list+'/add') 14 | input(type='hidden', name='_csrf', value=csrf) 15 | 16 | include ./form.pug 17 | 18 | .form-group 19 | button.btn.btn-primary(type="submit") Add subscriber 20 | = " " 21 | a.btn.btn-warning(href='/lists/view/'+list) Cancel 22 | -------------------------------------------------------------------------------- /views/lists/subscribers/edit.pug: -------------------------------------------------------------------------------- 1 | extends ../../layouts/main.pug 2 | 3 | block content 4 | 5 | if error 6 | +alert('danger', error) 7 | 8 | include ../list-header 9 | 10 | form(method='post', action='/lists/subscribers/'+list+'/edit') 11 | input(type='hidden', name='_csrf', value=csrf) 12 | input(type='hidden', name='subscriber', value=subscriber) 13 | 14 | include ./form.pug 15 | 16 | .float-right 17 | button.btn.btn-danger(type="button", data-toggle="modal", data-target="#mm-delete-confirm") Delete subscriber 18 | 19 | .form-group 20 | button.btn.btn-primary(type="submit") Save changes 21 | = " " 22 | a.btn.btn-warning(href='/lists/view/'+list) Cancel 23 | 24 | form(method='post', action='/lists/subscribers/'+list+'/delete') 25 | input(type='hidden', name='_csrf', value=csrf) 26 | input(type='hidden', name='subscriber', value=subscriber) 27 | +confirmDelete('mm-delete-confirm', 'Delete list', 'Are you sure you want to delete this subscriber?', 'No, cancel', 'Yes, delete') 28 | -------------------------------------------------------------------------------- /views/lists/subscribers/form.pug: -------------------------------------------------------------------------------- 1 | include ../../subscribers/form.pug 2 | 3 | div.form-group.form-check 4 | input.form-check-input#testSubscriber(type='checkbox', name='testSubscriber', checked=values.testSubscriber) 5 | label.form-check-label(for="testSubscriber") Test subscriber 6 | small.form-text.text-muted You can send preview emails to test subscribers before sending out to the entire list 7 | 8 | .form-group 9 | label(for='note') Note 10 | textarea#note.form-control(name='note', placeholder='Note', value=values.note, rows=3, class={'is-invalid': errors && errors.note})= values.note 11 | small.form-text.text-muted Subscribers do not see this info 12 | if errors && errors.note 13 | .invalid-feedback= errors.note 14 | 15 | .form-group 16 | label(for='status') Subscription status 17 | select#status.form-control(name='status', class={'is-invalid': errors && errors.status}) 18 | option(value='subscribed', selected=values.status==='subscribed') Subscribed 19 | option(value='unsubscribed', selected=values.status==='unsubscribed') Unsubscribed 20 | option(value='unconfirmed', selected=values.status==='unconfirmed') Unconfirmed 21 | option(value='bounced', selected=values.status==='bounced') Bounced 22 | if errors && errors.status 23 | .invalid-feedback= errors.status 24 | -------------------------------------------------------------------------------- /views/lists/view.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | 5 | include list-header 6 | 7 | form(method='get', action='/lists/view/' + list) 8 | div.form-row.align-items-center 9 | div.col-auto 10 | input.form-control.mb-2(type="search", name="query", value=query, placeholder="Search") 11 | div.col-auto 12 | button.btn.btn-primary.mb-2(type="submit") Search 13 | if query 14 | =" " 15 | a.btn.btn-warning.mb-2(href='/lists/view/'+list) Clear search 16 | 17 | if !subscribers.length 18 | if query 19 | p Nothing found for your query 20 | else 21 | p No subscribers added so far, 22 | = " " 23 | a(href='/lists/subscribers/'+list+'/add') add one here 24 | |. 25 | 26 | else 27 | 28 | table.table.mt-3 29 | thead 30 | tr 31 | th # 32 | th Name 33 | th Address 34 | th Status 35 | th Created 36 | th   37 | tbody 38 | for subscriberData in subscribers 39 | tr 40 | th= subscriberData.nr 41 | td= subscriberData.name 42 | if subscriberData.testSubscriber 43 | =" " 44 | span.badge.badge-secondary test 45 | td= subscriberData.email 46 | td= subscriberData.statusStr 47 | td= subscriberData.createdStr 48 | td 49 | a(href='/lists/subscribers/'+list+'/edit/'+subscriberData._id) Edit 50 | 51 | +paging(curpage, pages, pagingUrl) 52 | -------------------------------------------------------------------------------- /views/messages/add.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | 5 | if error 6 | +alert('danger', error) 7 | 8 | include message-header 9 | 10 | form(method='post', action='/messages/add') 11 | input(type='hidden', name='_csrf', value=csrf) 12 | 13 | include ./message-form.pug 14 | 15 | .form-group 16 | button.btn.btn-primary(type="submit") Create draft 17 | = " " 18 | a.btn.btn-warning(href='/lists') Cancel 19 | 20 | 21 | block append css 22 | if !values.useCodeEditor 23 | link(href='/quill/quill.snow.css', rel='stylesheet') 24 | 25 | block append scripts 26 | 27 | if !values.useCodeEditor 28 | script(src='/quill/quill.min.js') 29 | script(src='/editor.js') 30 | else 31 | script(src='/ace/ace.js') 32 | script. 33 | var editor = ace.edit("editor"); 34 | editor.setTheme("ace/theme/xcode"); 35 | editor.session.setMode("ace/mode/handlebars"); 36 | const editorElm = document.querySelector('#editordata'); 37 | editor.getSession().setValue(editordata.html); 38 | let editortimer = false; 39 | editor.getSession().on('change', function(){ 40 | clearTimeout(editortimer); 41 | editortimer = setTimeout(() => { 42 | editorElm.value = JSON.stringify({ 43 | html: editor.getSession().getValue(), 44 | changes: [] 45 | }); 46 | }, 500); 47 | }); 48 | 49 | document.querySelector('#mm-editor-insert-div').classList.remove('mm-hidden'); 50 | 51 | function setupEditorInsert(elm){ 52 | elm.addEventListener('click', function(e){ 53 | e.preventDefault(); 54 | editor.insert(elm.dataset.value) 55 | }, false) 56 | } 57 | 58 | var btns = document.querySelectorAll('.mm-editor-insert'); 59 | for(var i=0, len=btns.length; i=pages}) 22 | a.page-link(href=format(url, page+1)) Next 23 | 24 | 25 | mixin modal(id, title, text, canceltype, oktype, canceltext, oktext) 26 | div.modal(id=id, tabindex="-1", role="dialog") 27 | div.modal-dialog(role="document") 28 | div.modal-content 29 | div.modal-header 30 | h5.modal-title#mm-modal-title= title 31 | button.close(type="button", data-dismiss="modal", aria-label="Close") 32 | span(aria-hidden="true") × 33 | 34 | div.modal-body#mm-modal-body 35 | p= text 36 | 37 | div.modal-footer 38 | if canceltext 39 | button.btn(type="button", class="btn-"+canceltype, data-dismiss="modal")= canceltext 40 | if oktext 41 | button.btn(type="submit", class="btn-"+oktype)= oktext 42 | 43 | mixin confirm(id, title, text, canceltext, oktext) 44 | +modal(id, title, text, "secondary", "primary", canceltext, oktext) 45 | 46 | mixin confirmDelete(id, title, text, canceltext, oktext) 47 | +modal(id, title, text, "secondary", "danger", canceltext, oktext) 48 | -------------------------------------------------------------------------------- /views/partials/flash-messages.pug: -------------------------------------------------------------------------------- 1 | //- enumerate all pending flash messages 2 | each type in ['danger', 'success'] 3 | if messages[type] 4 | div 5 | each message in messages[type] 6 | +alert(type, message) //- see partials/alert.pug 7 | -------------------------------------------------------------------------------- /views/subscribers/change.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h2 Subscription updated 5 | 6 | p.lead Your subscription address for #{listData.name} was successfully updated. 7 | 8 | p 9 | a(href='/subscribers/edit/' + subscriber) Update your preferences 10 | -------------------------------------------------------------------------------- /views/subscribers/confirm.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3 Subscription confirmed 5 | 6 | p.lead Your subscription for #{listData.name} was successfully confirmed. 7 | 8 | p 9 | a.btn.btn-primary(href='/subscribers/edit/' + subscriber) Update your preferences 10 | -------------------------------------------------------------------------------- /views/subscribers/edit.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3 Update your preferences for #{listData.name} 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | form(method='post', action='/subscribers/edit') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | input(type='hidden', name='subscriber', value=subscriber) 12 | input(type='hidden', name='tz', value=values.tz, class={'mm-tz-detect': !values.tz}) 13 | 14 | include ./form.pug 15 | 16 | .form-group 17 | label(for='status') Subscription status 18 | select#status.form-control(name='status', class={'is-invalid': errors && errors.status}) 19 | option(value='subscribed', selected=values.status==='subscribed') 20 | if values.status==='subscribed' 21 | | Subscribed 22 | else 23 | | Subscribe me to this list 24 | option(value='unsubscribed', selected=values.status!=='subscribed') 25 | if values.status!=='subscribed' 26 | | Unsubscribed 27 | else 28 | | Unsubscribe me from this list 29 | if values.status==='bounced' 30 | if errors && errors.status 31 | .invalid-feedback= errors.status 32 | 33 | .form-group 34 | button.btn.btn-primary(type="submit") Update preferences 35 | = " or " 36 | a(href='/subscribers/unsubscribe/' + subscriber) Unsubscribe 37 | -------------------------------------------------------------------------------- /views/subscribers/error.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h2 #{status} Error 5 | p.lead= message 6 | if error 7 | pre= error.stack 8 | -------------------------------------------------------------------------------- /views/subscribers/form.pug: -------------------------------------------------------------------------------- 1 | for field in fields 2 | if !field.hidden 3 | .form-group 4 | label(for='fields['+field.key+']')= field.name 5 | if field.showHidden 6 | =" " 7 | small.text-muted (hidden for subscribers) 8 | if field.type === 'pgp' 9 | textarea.form-control.pgp-input(name='fields['+field.key+']', id='fields['+field.key+']', placeholder='Begins with "-----BEGIN PGP PUBLIC KEY BLOCK-----"', rows=5, class={'is-invalid': errors && errors[field.key]})= values.fields[field.key] 10 | else 11 | input.form-control(type=field.type, name='fields['+field.key+']', id='fields['+field.key+']', value=values.fields[field.key], required=field.required, class={'is-invalid': errors && errors[field.key]}) 12 | if field.help 13 | small.form-text.text-muted= field.help 14 | if errors && errors[field.key] 15 | .invalid-feedback= errors[field.key] 16 | -------------------------------------------------------------------------------- /views/subscribers/list.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | 5 | h3 Subscriptions for #{email} 6 | 7 | if !subscriptions.length 8 | 9 | p No subscriptions found 10 | 11 | else 12 | 13 | table.table.mt-3 14 | thead 15 | tr 16 | th # 17 | th List 18 | th Status 19 | th   20 | tbody 21 | for subscriptionData in subscriptions 22 | tr 23 | th= subscriptionData.nr 24 | td= subscriptionData.listData.name 25 | td= subscriptionData.statusStr 26 | td 27 | a(href='/subscribers/edit/'+subscriptionData._id) Edit 28 | 29 | +paging(curpage, pages, pagingUrl) 30 | -------------------------------------------------------------------------------- /views/subscribers/subscribe.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3 Subscribe to #{listData.name} 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | form.mm-captcha-form(method='post', action='/subscribers/subscribe') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | input(type='hidden', name='list', value=list) 12 | input(type='hidden', name='tz', value=values.tz, class={'mm-tz-detect': !values.tz}) 13 | input.mm-clear(type='hidden', name='e', value='1') 14 | 15 | include ./form.pug 16 | 17 | .form-group 18 | if recaptcha 19 | button.btn.btn-primary.g-recaptcha(data-sitekey=recaptcha, data-callback="onCreateSubmit") Subscribe 20 | else 21 | button.btn.btn-primary(type="submit") Subscribe 22 | -------------------------------------------------------------------------------- /views/subscribers/subscribed.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3 Subscribed to #{listData.name} 5 | 6 | if emailSent 7 | p Confirmation email message was sent to #{values.email}, you need to visit the link in order to activate your subscription 8 | else 9 | p Your subscription was updated 10 | -------------------------------------------------------------------------------- /views/subscribers/subscriptions.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3 List my subscriptions 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | p.lead Receive a link to list all your subscriptions in this server 10 | 11 | form.mm-captcha-form(method='post', action='/subscribers/subscriptions') 12 | input(type='hidden', name='_csrf', value=csrf) 13 | input.mm-clear(type='hidden', name='e', value='1') 14 | 15 | .form-group 16 | label(for='email') Email address 17 | input#email.form-control(type='email', name='email', placeholder='Your email', required=true, value=values.email, class={'is-invalid': errors && errors.email}) 18 | small.form-text.text-muted An email with link to your subscriptions will be sent to this address 19 | if errors && errors.email 20 | .invalid-feedback= errors.email 21 | 22 | .form-group 23 | if recaptcha 24 | button.btn.btn-primary.g-recaptcha(data-sitekey=recaptcha, data-callback="onCreateSubmit") Send link 25 | else 26 | button.btn.btn-primary(type="submit") Send link 27 | -------------------------------------------------------------------------------- /views/subscribers/unsubscribe.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3 Unsubscribe from #{listData.name} 5 | 6 | if error 7 | +alert('danger', error) 8 | 9 | form.mm-autosubmit(method='post', action='/subscribers/unsubscribe') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | input(type='hidden', name='subscriber', value=subscriber) 12 | 13 | .form-group 14 | label(for='email') Email address 15 | input#email.form-control(type='email', name='email', placeholder='Email address', readonly=true, disabled=true, value=values.email, class={'is-invalid': errors && errors.email}) 16 | if errors && errors.email 17 | .invalid-feedback= errors.email 18 | 19 | .form-group 20 | button.btn.btn-primary(type="submit") Unsubscribe 21 | -------------------------------------------------------------------------------- /views/subscribers/unsubscribed.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/subscribers.pug 2 | 3 | block content 4 | h3 Unsubscribed from #{listData.name} 5 | 6 | p.lead Your subscription for #{listData.name} was successfully cancelled. 7 | 8 | p 9 | a(href='/subscribers/edit/' + subscriber) Update your preferences 10 | -------------------------------------------------------------------------------- /views/templates/add.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | if error 5 | +alert('danger', error) 6 | 7 | include template-header 8 | 9 | form(method='post', action='/templates/add') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | 12 | include template-form 13 | 14 | .form-group 15 | button.btn.btn-primary(type='submit') Add new template 16 | = " " 17 | a.btn.btn-warning(href='/templates') Cancel 18 | 19 | 20 | block append scripts 21 | script(src='/ace/ace.js') 22 | 23 | script. 24 | var editor = ace.edit("editor"); 25 | editor.setTheme("ace/theme/xcode"); 26 | editor.session.setMode("ace/mode/handlebars"); 27 | 28 | var textarea = $('textarea#code').hide(); 29 | editor.getSession().setValue(textarea.val()); 30 | editor.getSession().on('change', function(){ 31 | textarea.val(editor.getSession().getValue()); 32 | }); 33 | 34 | function setupEditorInsert(elm){ 35 | elm.addEventListener('click', function(e){ 36 | e.preventDefault(); 37 | editor.insert(elm.dataset.value) 38 | }, false) 39 | } 40 | 41 | var btns = document.querySelectorAll('.mm-editor-insert'); 42 | for(var i=0, len=btns.length; iHandlebars syntax 24 | if errors && errors.code 25 | .invalid-feedback= errors.code 26 | -------------------------------------------------------------------------------- /views/templates/template-header.pug: -------------------------------------------------------------------------------- 1 | div.float-right 2 | a.btn.btn-info(href='/templates/add') Add new template 3 | 4 | h2 Templates 5 | 6 | p.lead This is where you can manage base templates for your messages. 7 | 8 | include template-menu.pug 9 | 10 | div.clearfix.mt-3 11 | -------------------------------------------------------------------------------- /views/templates/template-menu.pug: -------------------------------------------------------------------------------- 1 | nav.nav.nav-tabs 2 | li.nav-item 3 | a.nav-link(href='/templates', class={'active': templatePage=='list'}) Templates 4 | if templatePage=='manage' 5 | li.nav-item 6 | a.nav-link.active(href='#')= title 7 | -------------------------------------------------------------------------------- /views/users/add.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | if error 5 | +alert('danger', error) 6 | 7 | include user-header 8 | 9 | form(method='post', action='/users/add') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | 12 | include user-form 13 | 14 | .form-group 15 | button.btn.btn-primary(type='submit') Add new user 16 | = " " 17 | a.btn.btn-warning(href='/users') Cancel 18 | -------------------------------------------------------------------------------- /views/users/edit.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | if error 5 | +alert('danger', error) 6 | 7 | include user-header 8 | 9 | form(method='post', action='/users/edit') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | input(type='hidden', name='user', value=userData._id) 12 | 13 | include user-form 14 | 15 | .float-right 16 | button.btn.btn-danger(type="button", data-toggle="modal", data-target="#mm-delete-confirm") Delete user 17 | 18 | .form-group 19 | button.btn.btn-primary(type="submit") Save changes 20 | = " " 21 | a.btn.btn-warning(href='/lists/view/'+list) Cancel 22 | 23 | form(method='post', action='/users/delete') 24 | input(type='hidden', name='_csrf', value=csrf) 25 | input(type='hidden', name='user', value=userData._id) 26 | +confirmDelete('mm-delete-confirm', 'Delete list', 'Are you sure you want to delete this user? This action is irreversible.', 'No, cancel', 'Yes, delete') 27 | -------------------------------------------------------------------------------- /views/users/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | 5 | include user-header 6 | 7 | form(method='get', action='/users') 8 | div.form-row.align-items-center 9 | div.col-auto 10 | input.form-control.mb-2(type="search", name="query", value=query, placeholder="Search") 11 | div.col-auto 12 | button.btn.btn-primary.mb-2(type="submit") Search 13 | if query 14 | =" " 15 | a.btn.btn-warning.mb-2(href='/users') Clear search 16 | 17 | if !users.length 18 | 19 | p Nothing found for your query 20 | 21 | else 22 | 23 | table.table.mt-3 24 | thead 25 | tr 26 | th # 27 | th Name 28 | th Address 29 | th Created 30 | th   31 | tbody 32 | for userData in users 33 | tr 34 | th= userData.nr 35 | td= userData.name 36 | td= userData.email 37 | td= userData.createdStr 38 | td 39 | a(href='/users/edit/'+userData._id) Edit 40 | 41 | +paging(curpage, pages, pagingUrl) 42 | -------------------------------------------------------------------------------- /views/users/settings.pug: -------------------------------------------------------------------------------- 1 | extends ../layouts/main.pug 2 | 3 | block content 4 | if error 5 | +alert('danger', error) 6 | 7 | include user-header 8 | 9 | form(method='post', action='/users/settings') 10 | input(type='hidden', name='_csrf', value=csrf) 11 | 12 | div.form-group.form-check 13 | input.form-check-input#allow-join(type='checkbox', name='global_user_disableJoin', checked=values.global_user_disableJoin) 14 | label.form-check-label(for="allow-join") Disable public signups 15 | small.form-text.text-muted If not checked then anyone can click on the "Join" link and create an account 16 | 17 | .form-group 18 | button.btn.btn-primary(type='submit') Update settings 19 | = " " 20 | a.btn.btn-warning(href='/users') Cancel 21 | -------------------------------------------------------------------------------- /views/users/user-form.pug: -------------------------------------------------------------------------------- 1 | .form-group 2 | label(for='name') User name 3 | input#name.form-control(type='name', name='name', placeholder='User name', required=true, value=values.name, class={'is-invalid': errors && errors.name}) 4 | if errors && errors.name 5 | .invalid-feedback= errors.name 6 | 7 | .form-group 8 | label(for='email') Email address 9 | input#email.form-control(type='email', name='email', placeholder='User email', required=true, value=values.email, class={'is-invalid': errors && errors.email}) 10 | small.form-text.text-muted Account password is sent to this email address 11 | if errors && errors.email 12 | .invalid-feedback= errors.email 13 | 14 | .form-group 15 | label(for='locale') Locale 16 | select#locale.form-control(name='locale', class={'is-invalid': errors && errors.locale}) 17 | for item in locales 18 | option(value=item.code, selected=values.locale == item.code) #{item.language} (#{item.country}) 19 | small.form-text.text-muted Date and number format 20 | if errors && errors.locale 21 | .invalid-feedback= errors.locale 22 | 23 | .form-group 24 | label(for='tz') Timezone 25 | select#tz.form-control(name='tz', class={'is-invalid': errors && errors.tz}, class={'mm-tz-detect': !values.tz}) 26 | for timezone in timezones 27 | option(value=timezone, selected=values.tz == timezone) #{timezone} 28 | small.form-text.text-muted User timezone 29 | if errors && errors.tz 30 | .invalid-feedback= errors.tz 31 | 32 | .form-group 33 | label(for='status') Status 34 | select#status.form-control(name='status', class={'is-invalid': errors && errors.status}) 35 | for item in statuses 36 | option(value=item.key, selected=values.status == item.key) #{item.name} 37 | if errors && errors.status 38 | .invalid-feedback= errors.status 39 | -------------------------------------------------------------------------------- /views/users/user-header.pug: -------------------------------------------------------------------------------- 1 | div.float-right 2 | a.btn.btn-info(href='/users/add') Add new user 3 | 4 | h2 Users 5 | 6 | p.lead This is where you can manage registered users for the service. Every user has their own set of lists and subscribers. 7 | 8 | include user-menu.pug 9 | 10 | div.clearfix.mt-3 11 | -------------------------------------------------------------------------------- /views/users/user-menu.pug: -------------------------------------------------------------------------------- 1 | nav.nav.nav-tabs 2 | li.nav-item 3 | a.nav-link(href='/users', class={'active': userPage=='list'}) Users 4 | li.nav-item 5 | a.nav-link(href='/users/settings', class={'active': userPage=='settings'}) Settings 6 | if userPage=='manage' 7 | li.nav-item 8 | a.nav-link.active(href='#')= title 9 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | //const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 5 | 6 | const mode = process.env.NODE_ENV || 'development'; 7 | 8 | module.exports = { 9 | mode, 10 | 11 | watch: false, 12 | 13 | devtool: 'source-map', 14 | 15 | entry: './sources/index.js', 16 | 17 | output: { 18 | filename: 'bundle.js', 19 | path: path.resolve(__dirname, 'public', 'app') 20 | } 21 | //, 22 | //plugins: [new UglifyJsPlugin()] 23 | }; 24 | --------------------------------------------------------------------------------