Moved campaign attributes to use Select2 instead of Typeahead.js

pull/539/merge
Jordan Wright 2017-01-29 17:56:38 -06:00
parent ea7bb04156
commit ed980a0861
12 changed files with 148 additions and 1052 deletions

View File

@ -41,13 +41,12 @@ gulp.task('build', function() {
vendor_directory + 'jquery.dataTables.min.js',
vendor_directory + 'dataTables.bootstrap.js',
vendor_directory + 'datetime-moment.js',
vendor_directory + 'hogan.js',
vendor_directory + 'jquery.ui.widget.js',
vendor_directory + 'jquery.fileupload.js',
vendor_directory + 'jquery.iframe-transport.js',
vendor_directory + 'sweetalert2.min.js',
vendor_directory + 'typeahead.min.js',
vendor_directory + 'bootstrap-datetime.js'
vendor_directory + 'bootstrap-datetime.js',
vendor_directory + 'select2.min.js'
])
.pipe(concat('vendor.js'))
.pipe(rename({
@ -74,7 +73,9 @@ gulp.task('build', function() {
css_directory + 'chartist.min.css',
css_directory + 'bootstrap-datetime.css',
css_directory + 'checkbox.css',
css_directory + 'sweetalert2.min.css'
css_directory + 'sweetalert2.min.css',
css_directory + 'select2.min.css',
css_directory + 'select2-bootstrap.min.css'
])
.pipe(cleanCSS({compatibilty: 'ie9'}))
.pipe(concat('gophish.css'))

File diff suppressed because one or more lines are too long

10
static/css/main.css vendored
View File

@ -531,3 +531,13 @@ table.dataTable{
.btn-blue:hover{
background-color:#64a1d6;
}
.select2-container--bootstrap .select2-selection--single .select2-selection__rendered {
font-size: 15px !important;
}
.select2-container--bootstrap .select2-selection--single {
height: 42px !important;
padding: 8px 12px !important;
}
.input-group-btn .btn {
line-height:20px !important;
}

1
static/css/select2-bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

1
static/css/select2.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -27,22 +27,21 @@ function launch() {
preConfirm: function() {
return new Promise(function(resolve, reject) {
groups = []
$.each($("#groupTable").DataTable().rows().data(), function(i, group) {
groups.push({
name: unescapeHtml(group[0])
})
$("#users").select2("data").forEach(function(group){
groups.push({name: group.text});
})
// Validate our fields
campaign = {
name: $("#name").val(),
template: {
name: $("#template").val()
name: $("#template").select2("data")[0].text
},
url: $("#url").val(),
page: {
name: $("#page").val()
name: $("#page").select2("data")[0].text
},
smtp: {
name: $("#profile").val()
name: $("#profile").select2("data")[0].text
},
launch_date: moment($("#launch_date").val(), "MM/DD/YYYY hh:mm a").format(),
groups: groups
@ -76,7 +75,7 @@ function launch() {
function sendTestEmail() {
var test_email_request = {
template: {
name: $("#template").val()
name: $("#template").select2("data")[0].text
},
first_name: $("input[name=to_first_name]").val(),
last_name: $("input[name=to_last_name]").val(),
@ -84,10 +83,10 @@ function sendTestEmail() {
position: $("input[name=to_position]").val(),
url: $("#url").val(),
page: {
name: $("#page").val()
name: $("#page").select2("data")[0].text
},
smtp: {
name: $("#profile").val()
name: $("#profile").select2("data")[0].text
}
}
btnHtml = $("#sendTestModalSubmit").html()
@ -107,15 +106,14 @@ function sendTestEmail() {
}
function dismiss() {
$("#modal\\.flashes").empty()
$("#name").val("")
$("#template").val("")
$("#page").val("")
$("#url").val("")
$("#profile").val("")
$("#groupSelect").val("")
$("#modal").modal('hide')
$("#groupTable").dataTable().DataTable().clear().draw()
$("#modal\\.flashes").empty();
$("#name").val("");
$("#template").val("").change();
$("#page").val("").change();
$("#url").val("");
$("#profile").val("").change();
$("#users").val("").change();
$("#modal").modal('hide');
}
function deleteCampaign(idx) {
@ -152,100 +150,104 @@ function deleteCampaign(idx) {
})
}
function edit(campaign) {
// Clear the bloodhound instance
group_bh.clear();
template_bh.clear();
page_bh.clear();
profile_bh.clear();
if (campaign == "new") {
function setupOptions() {
api.groups.get()
.success(function(groups) {
if (groups.length == 0) {
modalError("No groups found!")
return false;
} else {
group_bh.add(groups)
var group_s2 = $.map(groups, function(obj) {
obj.text = obj.name
return obj
});
$("#users.form-control").select2({
placeholder: "Select Groups",
data: group_s2,
});
}
})
});
api.templates.get()
.success(function(templates) {
if (templates.length == 0) {
modalError("No templates found!")
return false
} else {
template_bh.add(templates)
var template_s2 = $.map(templates, function(obj) {
obj.text = obj.name
return obj
});
$("#template.form-control").select2({
placeholder: "Select a Template",
data: template_s2,
});
}
})
});
api.pages.get()
.success(function(pages) {
if (pages.length == 0) {
modalError("No pages found!")
return false
} else {
page_bh.add(pages)
var page_s2 = $.map(pages, function(obj) {
obj.text = obj.name
return obj
});
$("#page.form-control").select2({
placeholder: "Select a Landing Page",
data: page_s2,
});
}
})
});
api.SMTP.get()
.success(function(profiles) {
if (profiles.length == 0) {
modalError("No profiles found!")
return false
} else {
profile_bh.add(profiles)
}
})
var profile_s2 = $.map(profiles, function(obj) {
obj.text = obj.name
return obj
});
$("#profile.form-control").select2({
placeholder: "Select a Sending Profile",
data: profile_s2,
});
}
});
}
function edit(campaign) {
setupOptions();
}
function copy(idx) {
group_bh.clear();
template_bh.clear();
page_bh.clear();
profile_bh.clear();
api.groups.get()
.success(function(groups) {
if (groups.length == 0) {
modalError("No groups found!")
return false;
} else {
group_bh.add(groups)
}
})
api.templates.get()
.success(function(templates) {
if (templates.length == 0) {
modalError("No templates found!")
return false
} else {
template_bh.add(templates)
}
})
api.pages.get()
.success(function(pages) {
if (pages.length == 0) {
modalError("No pages found!")
return false
} else {
page_bh.add(pages)
}
})
api.SMTP.get()
.success(function(profiles) {
if (profiles.length == 0) {
modalError("No profiles found!")
return false
} else {
profile_bh.add(profiles)
}
})
setupOptions();
// Set our initial values
api.campaignId.get(campaigns[idx].id)
.success(function(campaign) {
$("#name").val("Copy of " + campaign.name)
$("#template").val(campaign.template.name)
$("#page").val(campaign.page.name)
$("#profile").val(campaign.smtp.name)
if (!campaign.template.id) {
$("#template").select2({
placeholder: campaign.template.name
});
} else {
$("#template").select2("val", campaign.template.id.toString());
}
if (!campaign.page.id) {
$("#page").select2({
placeholder: campaign.page.name
});
} else {
$("#page").select2("val", campaign.page.id.toString());
}
if (!campaign.smtp.id) {
$("#profile").select2({
placeholder: campaign.smtp.name
});
} else {
$("#profile").select2("val", campaign.smtp.id.toString());
}
$("#url").val(campaign.url)
})
.error(function(data) {
@ -285,20 +287,10 @@ $(document).ready(function() {
$('.modal-backdrop').not('.fv-modal-stack').css('z-index', 1039 + (10 * $('body').data('fv_open_modals')));
$('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack');
});
$.fn.modal.Constructor.prototype.enforceFocus = function() {
$(document)
.off('focusin.bs.modal') // guard against infinite focus loop
.on('focusin.bs.modal', $.proxy(function(e) {
if (
this.$element[0] !== e.target && !this.$element.has(e.target).length
// CKEditor compatibility fix start.
&& !$(e.target).closest('.cke_dialog, .cke').length
// CKEditor compatibility fix end.
) {
this.$element.trigger('focus');
}
}, this));
};
// Scrollbar fix - https://stackoverflow.com/questions/19305821/multiple-modals-overlay
$(document).on('hidden.bs.modal', '.modal', function () {
$('.modal:visible').length && $(document.body).addClass('modal-open');
});
$('#modal').on('hidden.bs.modal', function(event) {
dismiss()
});
@ -343,154 +335,8 @@ $(document).ready(function() {
$("#loading").hide()
errorFlash("Error fetching campaigns")
})
$("#groupForm").submit(function() {
// Add row to group table.
var newRow = groupTable.row.add([
escapeHtml($("#groupSelect").val()),
'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'
]).draw().node();
// Set event handler for removing row from group table.
$(newRow).on("click", "span>i.fa-trash-o", function() {
groupTable.row($(this).parents('tr'))
.remove()
.draw();
});
// Clear user input.
$("#groupSelect").typeahead('val', "");
return false;
});
// Create the group typeahead objects
groupTable = $("#groupTable").DataTable({
columnDefs: [{
orderable: false,
targets: "no-sort"
}]
})
group_bh = new Bloodhound({
datumTokenizer: function(g) {
return Bloodhound.tokenizers.whitespace(g.name)
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: []
})
group_bh.initialize()
$("#groupSelect.typeahead.form-control").typeahead({
hint: true,
highlight: true,
minLength: 1
}, {
name: "groups",
source: group_bh,
templates: {
empty: function(data) {
return '<div class="tt-suggestion">No groups matched that query</div>'
},
suggestion: function(data) {
return '<div>' + escapeHtml(data.name) + '</div>'
}
}
})
.bind('typeahead:select', function(ev, group) {
// Add selected group.
$("#groupSelect").typeahead('val', group.name);
$("#groupForm").submit();
})
.bind('typeahead:autocomplete', function(ev, group) {
$("#groupSelect").typeahead('val', group.name)
});
// Create the template typeahead objects
template_bh = new Bloodhound({
datumTokenizer: function(t) {
return Bloodhound.tokenizers.whitespace(t.name)
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: []
})
template_bh.initialize()
$("#template.typeahead.form-control").typeahead({
hint: true,
highlight: true,
minLength: 1
}, {
name: "templates",
source: template_bh,
templates: {
empty: function(data) {
return '<div class="tt-suggestion">No templates matched that query</div>'
},
suggestion: function(data) {
return '<div>' + escapeHtml(data.name) + '</div>'
}
}
})
.bind('typeahead:select', function(ev, template) {
$("#template").typeahead('val', template.name)
})
.bind('typeahead:autocomplete', function(ev, template) {
$("#template").typeahead('val', template.name)
});
// Create the landing page typeahead objects
page_bh = new Bloodhound({
datumTokenizer: function(p) {
return Bloodhound.tokenizers.whitespace(p.name)
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: []
})
page_bh.initialize()
$("#page.typeahead.form-control").typeahead({
hint: true,
highlight: true,
minLength: 1
}, {
name: "pages",
source: page_bh,
templates: {
empty: function(data) {
return '<div class="tt-suggestion">No pages matched that query</div>'
},
suggestion: function(data) {
return '<div>' + escapeHtml(data.name) + '</div>'
}
}
})
.bind('typeahead:select', function(ev, page) {
$("#page").typeahead('val', page.name)
})
.bind('typeahead:autocomplete', function(ev, page) {
$("#page").typeahead('val', page.name)
});
// Create the sending profile typeahead objects
profile_bh = new Bloodhound({
datumTokenizer: function(s) {
return Bloodhound.tokenizers.whitespace(s.name)
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: []
})
profile_bh.initialize()
$("#profile.typeahead.form-control").typeahead({
hint: true,
highlight: true,
minLength: 1
}, {
name: "profiles",
source: profile_bh,
templates: {
empty: function(data) {
return '<div class="tt-suggestion">No profiles matched that query</div>'
},
suggestion: function(data) {
return '<div>' + escapeHtml(data.name) + '</div>'
}
}
})
.bind('typeahead:select', function(ev, profile) {
$("#profile").typeahead('val', profile.name)
})
.bind('typeahead:autocomplete', function(ev, profile) {
$("#profile").typeahead('val', profile.name)
});
// Select2 Defaults
$.fn.select2.defaults.set("width", "100%");
$.fn.select2.defaults.set("dropdownParent", $("#modal_body"));
$.fn.select2.defaults.set("theme", "bootstrap");
})

View File

@ -1,750 +0,0 @@
/*!
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Hogan = {};
(function (Hogan) {
Hogan.Template = function (codeObj, text, compiler, options) {
codeObj = codeObj || {};
this.r = codeObj.code || this.r;
this.c = compiler;
this.options = options || {};
this.text = text || '';
this.partials = codeObj.partials || {};
this.subs = codeObj.subs || {};
this.buf = '';
}
Hogan.Template.prototype = {
// render: replaced by generated code.
r: function (context, partials, indent) { return ''; },
// variable escaping
v: hoganEscape,
// triple stache
t: coerceToString,
render: function render(context, partials, indent) {
return this.ri([context], partials || {}, indent);
},
// render internal -- a hook for overrides that catches partials too
ri: function (context, partials, indent) {
return this.r(context, partials, indent);
},
// ensurePartial
ep: function(symbol, partials) {
var partial = this.partials[symbol];
// check to see that if we've instantiated this partial before
var template = partials[partial.name];
if (partial.instance && partial.base == template) {
return partial.instance;
}
if (typeof template == 'string') {
if (!this.c) {
throw new Error("No compiler available.");
}
template = this.c.compile(template, this.options);
}
if (!template) {
return null;
}
// We use this to check whether the partials dictionary has changed
this.partials[symbol].base = template;
if (partial.subs) {
// Make sure we consider parent template now
if (!partials.stackText) partials.stackText = {};
for (key in partial.subs) {
if (!partials.stackText[key]) {
partials.stackText[key] = (this.activeSub !== undefined && partials.stackText[this.activeSub]) ? partials.stackText[this.activeSub] : this.text;
}
}
template = createSpecializedPartial(template, partial.subs, partial.partials,
this.stackSubs, this.stackPartials, partials.stackText);
}
this.partials[symbol].instance = template;
return template;
},
// tries to find a partial in the current scope and render it
rp: function(symbol, context, partials, indent) {
var partial = this.ep(symbol, partials);
if (!partial) {
return '';
}
return partial.ri(context, partials, indent);
},
// render a section
rs: function(context, partials, section) {
var tail = context[context.length - 1];
if (!isArray(tail)) {
section(context, partials, this);
return;
}
for (var i = 0; i < tail.length; i++) {
context.push(tail[i]);
section(context, partials, this);
context.pop();
}
},
// maybe start a section
s: function(val, ctx, partials, inverted, start, end, tags) {
var pass;
if (isArray(val) && val.length === 0) {
return false;
}
if (typeof val == 'function') {
val = this.ms(val, ctx, partials, inverted, start, end, tags);
}
pass = !!val;
if (!inverted && pass && ctx) {
ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
}
return pass;
},
// find values with dotted names
d: function(key, ctx, partials, returnFound) {
var found,
names = key.split('.'),
val = this.f(names[0], ctx, partials, returnFound),
doModelGet = this.options.modelGet,
cx = null;
if (key === '.' && isArray(ctx[ctx.length - 2])) {
val = ctx[ctx.length - 1];
} else {
for (var i = 1; i < names.length; i++) {
found = findInScope(names[i], val, doModelGet);
if (found != null) {
cx = val;
val = found;
} else {
val = '';
}
}
}
if (returnFound && !val) {
return false;
}
if (!returnFound && typeof val == 'function') {
ctx.push(cx);
val = this.mv(val, ctx, partials);
ctx.pop();
}
return val;
},
// find values with normal names
f: function(key, ctx, partials, returnFound) {
var val = false,
v = null,
found = false,
doModelGet = this.options.modelGet;
for (var i = ctx.length - 1; i >= 0; i--) {
v = ctx[i];
val = findInScope(key, v, doModelGet);
if (val != null) {
found = true;
break;
}
}
if (!found) {
return (returnFound) ? false : "";
}
if (!returnFound && typeof val == 'function') {
val = this.mv(val, ctx, partials);
}
return val;
},
// higher order templates
ls: function(func, cx, partials, text, tags) {
var oldTags = this.options.delimiters;
this.options.delimiters = tags;
this.b(this.ct(coerceToString(func.call(cx, text)), cx, partials));
this.options.delimiters = oldTags;
return false;
},
// compile text
ct: function(text, cx, partials) {
if (this.options.disableLambda) {
throw new Error('Lambda features disabled.');
}
return this.c.compile(text, this.options).render(cx, partials);
},
// template result buffering
b: function(s) { this.buf += s; },
fl: function() { var r = this.buf; this.buf = ''; return r; },
// method replace section
ms: function(func, ctx, partials, inverted, start, end, tags) {
var textSource,
cx = ctx[ctx.length - 1],
result = func.call(cx);
if (typeof result == 'function') {
if (inverted) {
return true;
} else {
textSource = (this.activeSub && this.subsText && this.subsText[this.activeSub]) ? this.subsText[this.activeSub] : this.text;
return this.ls(result, cx, partials, textSource.substring(start, end), tags);
}
}
return result;
},
// method replace variable
mv: function(func, ctx, partials) {
var cx = ctx[ctx.length - 1];
var result = func.call(cx);
if (typeof result == 'function') {
return this.ct(coerceToString(result.call(cx)), cx, partials);
}
return result;
},
sub: function(name, context, partials, indent) {
var f = this.subs[name];
if (f) {
this.activeSub = name;
f(context, partials, this, indent);
this.activeSub = false;
}
}
};
//Find a key in an object
function findInScope(key, scope, doModelGet) {
var val, checkVal;
if (scope && typeof scope == 'object') {
if (scope[key] != null) {
val = scope[key];
// try lookup with get for backbone or similar model data
} else if (doModelGet && scope.get && typeof scope.get == 'function') {
val = scope.get(key);
}
}
return val;
}
function createSpecializedPartial(instance, subs, partials, stackSubs, stackPartials, stackText) {
function PartialTemplate() {};
PartialTemplate.prototype = instance;
function Substitutions() {};
Substitutions.prototype = instance.subs;
var key;
var partial = new PartialTemplate();
partial.subs = new Substitutions();
partial.subsText = {}; //hehe. substext.
partial.buf = '';
stackSubs = stackSubs || {};
partial.stackSubs = stackSubs;
partial.subsText = stackText;
for (key in subs) {
if (!stackSubs[key]) stackSubs[key] = subs[key];
}
for (key in stackSubs) {
partial.subs[key] = stackSubs[key];
}
stackPartials = stackPartials || {};
partial.stackPartials = stackPartials;
for (key in partials) {
if (!stackPartials[key]) stackPartials[key] = partials[key];
}
for (key in stackPartials) {
partial.partials[key] = stackPartials[key];
}
return partial;
}
var rAmp = /&/g,
rLt = /</g,
rGt = />/g,
rApos = /\'/g,
rQuot = /\"/g,
hChars = /[&<>\"\']/;
function coerceToString(val) {
return String((val === null || val === undefined) ? '' : val);
}
function hoganEscape(str) {
str = coerceToString(str);
return hChars.test(str) ?
str
.replace(rAmp, '&amp;')
.replace(rLt, '&lt;')
.replace(rGt, '&gt;')
.replace(rApos, '&#39;')
.replace(rQuot, '&quot;') :
str;
}
var isArray = Array.isArray || function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
};
})(typeof exports !== 'undefined' ? exports : Hogan);
(function (Hogan) {
// Setup regex assignments
// remove whitespace according to Mustache spec
var rIsWhitespace = /\S/,
rQuot = /\"/g,
rNewline = /\n/g,
rCr = /\r/g,
rSlash = /\\/g;
Hogan.tags = {
'#': 1, '^': 2, '<': 3, '$': 4,
'/': 5, '!': 6, '>': 7, '=': 8, '_v': 9,
'{': 10, '&': 11, '_t': 12
};
Hogan.scan = function scan(text, delimiters) {
var len = text.length,
IN_TEXT = 0,
IN_TAG_TYPE = 1,
IN_TAG = 2,
state = IN_TEXT,
tagType = null,
tag = null,
buf = '',
tokens = [],
seenTag = false,
i = 0,
lineStart = 0,
otag = '{{',
ctag = '}}';
function addBuf() {
if (buf.length > 0) {
tokens.push({tag: '_t', text: new String(buf)});
buf = '';
}
}
function lineIsWhitespace() {
var isAllWhitespace = true;
for (var j = lineStart; j < tokens.length; j++) {
isAllWhitespace =
(Hogan.tags[tokens[j].tag] < Hogan.tags['_v']) ||
(tokens[j].tag == '_t' && tokens[j].text.match(rIsWhitespace) === null);
if (!isAllWhitespace) {
return false;
}
}
return isAllWhitespace;
}
function filterLine(haveSeenTag, noNewLine) {
addBuf();
if (haveSeenTag && lineIsWhitespace()) {
for (var j = lineStart, next; j < tokens.length; j++) {
if (tokens[j].text) {
if ((next = tokens[j+1]) && next.tag == '>') {
// set indent to token value
next.indent = tokens[j].text.toString()
}
tokens.splice(j, 1);
}
}
} else if (!noNewLine) {
tokens.push({tag:'\n'});
}
seenTag = false;
lineStart = tokens.length;
}
function changeDelimiters(text, index) {
var close = '=' + ctag,
closeIndex = text.indexOf(close, index),
delimiters = trim(
text.substring(text.indexOf('=', index) + 1, closeIndex)
).split(' ');
otag = delimiters[0];
ctag = delimiters[delimiters.length - 1];
return closeIndex + close.length - 1;
}
if (delimiters) {
delimiters = delimiters.split(' ');
otag = delimiters[0];
ctag = delimiters[1];
}
for (i = 0; i < len; i++) {
if (state == IN_TEXT) {
if (tagChange(otag, text, i)) {
--i;
addBuf();
state = IN_TAG_TYPE;
} else {
if (text.charAt(i) == '\n') {
filterLine(seenTag);
} else {
buf += text.charAt(i);
}
}
} else if (state == IN_TAG_TYPE) {
i += otag.length - 1;
tag = Hogan.tags[text.charAt(i + 1)];
tagType = tag ? text.charAt(i + 1) : '_v';
if (tagType == '=') {
i = changeDelimiters(text, i);
state = IN_TEXT;
} else {
if (tag) {
i++;
}
state = IN_TAG;
}
seenTag = i;
} else {
if (tagChange(ctag, text, i)) {
tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,
i: (tagType == '/') ? seenTag - otag.length : i + ctag.length});
buf = '';
i += ctag.length - 1;
state = IN_TEXT;
if (tagType == '{') {
if (ctag == '}}') {
i++;
} else {
cleanTripleStache(tokens[tokens.length - 1]);
}
}
} else {
buf += text.charAt(i);
}
}
}
filterLine(seenTag, true);
return tokens;
}
function cleanTripleStache(token) {
if (token.n.substr(token.n.length - 1) === '}') {
token.n = token.n.substring(0, token.n.length - 1);
}
}
function trim(s) {
if (s.trim) {
return s.trim();
}
return s.replace(/^\s*|\s*$/g, '');
}
function tagChange(tag, text, index) {
if (text.charAt(index) != tag.charAt(0)) {
return false;
}
for (var i = 1, l = tag.length; i < l; i++) {
if (text.charAt(index + i) != tag.charAt(i)) {
return false;
}
}
return true;
}
// the tags allowed inside super templates
var allowedInSuper = {'_t': true, '\n': true, '$': true, '/': true};
function buildTree(tokens, kind, stack, customTags) {
var instructions = [],
opener = null,
tail = null,
token = null;
tail = stack[stack.length - 1];
while (tokens.length > 0) {
token = tokens.shift();
if (tail && tail.tag == '<' && !(token.tag in allowedInSuper)) {
throw new Error('Illegal content in < super tag.');
}
if (Hogan.tags[token.tag] <= Hogan.tags['$'] || isOpener(token, customTags)) {
stack.push(token);
token.nodes = buildTree(tokens, token.tag, stack, customTags);
} else if (token.tag == '/') {
if (stack.length === 0) {
throw new Error('Closing tag without opener: /' + token.n);
}
opener = stack.pop();
if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
}
opener.end = token.i;
return instructions;
} else if (token.tag == '\n') {
token.last = (tokens.length == 0) || (tokens[0].tag == '\n');
}
instructions.push(token);
}
if (stack.length > 0) {
throw new Error('missing closing tag: ' + stack.pop().n);
}
return instructions;
}
function isOpener(token, tags) {
for (var i = 0, l = tags.length; i < l; i++) {
if (tags[i].o == token.n) {
token.tag = '#';
return true;
}
}
}
function isCloser(close, open, tags) {
for (var i = 0, l = tags.length; i < l; i++) {
if (tags[i].c == close && tags[i].o == open) {
return true;
}
}
}
function stringifySubstitutions(obj) {
var items = [];
for (var key in obj) {
items.push('"' + esc(key) + '": function(c,p,t,i) {' + obj[key] + '}');
}
return "{ " + items.join(",") + " }";
}
function stringifyPartials(codeObj) {
var partials = [];
for (var key in codeObj.partials) {
partials.push('"' + esc(key) + '":{name:"' + esc(codeObj.partials[key].name) + '", ' + stringifyPartials(codeObj.partials[key]) + "}");
}
return "partials: {" + partials.join(",") + "}, subs: " + stringifySubstitutions(codeObj.subs);
}
Hogan.stringify = function(codeObj, text, options) {
return "{code: function (c,p,i) { " + Hogan.wrapMain(codeObj.code) + " }," + stringifyPartials(codeObj) + "}";
}
var serialNo = 0;
Hogan.generate = function(tree, text, options) {
serialNo = 0;
var context = { code: '', subs: {}, partials: {} };
Hogan.walk(tree, context);
if (options.asString) {
return this.stringify(context, text, options);
}
return this.makeTemplate(context, text, options);
}
Hogan.wrapMain = function(code) {
return 'var t=this;t.b(i=i||"");' + code + 'return t.fl();';
}
Hogan.template = Hogan.Template;
Hogan.makeTemplate = function(codeObj, text, options) {
var template = this.makePartials(codeObj);
template.code = new Function('c', 'p', 'i', this.wrapMain(codeObj.code));
return new this.template(template, text, this, options);
}
Hogan.makePartials = function(codeObj) {
var key, template = {subs: {}, partials: codeObj.partials, name: codeObj.name};
for (key in template.partials) {
template.partials[key] = this.makePartials(template.partials[key]);
}
for (key in codeObj.subs) {
template.subs[key] = new Function('c', 'p', 't', 'i', codeObj.subs[key]);
}
return template;
}
function esc(s) {
return s.replace(rSlash, '\\\\')
.replace(rQuot, '\\\"')
.replace(rNewline, '\\n')
.replace(rCr, '\\r');
}
function chooseMethod(s) {
return (~s.indexOf('.')) ? 'd' : 'f';
}
function createPartial(node, context) {
var prefix = "<" + (context.prefix || "");
var sym = prefix + node.n + serialNo++;
context.partials[sym] = {name: node.n, partials: {}};
context.code += 't.b(t.rp("' + esc(sym) + '",c,p,"' + (node.indent || '') + '"));';
return sym;
}
Hogan.codegen = {
'#': function(node, context) {
context.code += 'if(t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),' +
'c,p,0,' + node.i + ',' + node.end + ',"' + node.otag + " " + node.ctag + '")){' +
't.rs(c,p,' + 'function(c,p,t){';
Hogan.walk(node.nodes, context);
context.code += '});c.pop();}';
},
'^': function(node, context) {
context.code += 'if(!t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),c,p,1,0,0,"")){';
Hogan.walk(node.nodes, context);
context.code += '};';
},
'>': createPartial,
'<': function(node, context) {
var ctx = {partials: {}, code: '', subs: {}, inPartial: true};
Hogan.walk(node.nodes, ctx);
var template = context.partials[createPartial(node, context)];
template.subs = ctx.subs;
template.partials = ctx.partials;
},
'$': function(node, context) {
var ctx = {subs: {}, code: '', partials: context.partials, prefix: node.n};
Hogan.walk(node.nodes, ctx);
context.subs[node.n] = ctx.code;
if (!context.inPartial) {
context.code += 't.sub("' + esc(node.n) + '",c,p,i);';
}
},
'\n': function(node, context) {
context.code += write('"\\n"' + (node.last ? '' : ' + i'));
},
'_v': function(node, context) {
context.code += 't.b(t.v(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
},
'_t': function(node, context) {
context.code += write('"' + esc(node.text) + '"');
},
'{': tripleStache,
'&': tripleStache
}
function tripleStache(node, context) {
context.code += 't.b(t.t(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
}
function write(s) {
return 't.b(' + s + ');';
}
Hogan.walk = function(nodelist, context) {
var func;
for (var i = 0, l = nodelist.length; i < l; i++) {
func = Hogan.codegen[nodelist[i].tag];
func && func(nodelist[i], context);
}
return context;
}
Hogan.parse = function(tokens, text, options) {
options = options || {};
return buildTree(tokens, '', [], options.sectionTags || []);
}
Hogan.cache = {};
Hogan.cacheKey = function(text, options) {
return [text, !!options.asString, !!options.disableLambda, options.delimiters, !!options.modelGet].join('||');
}
Hogan.compile = function(text, options) {
options = options || {};
var key = Hogan.cacheKey(text, options);
var template = this.cache[key];
if (template) {
var partials = template.partials;
for (var name in partials) {
delete partials[name].instance;
}
return template;
}
template = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
return this.cache[key] = template;
}
})(typeof exports !== 'undefined' ? exports : Hogan);

3
static/js/src/vendor/select2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -68,45 +68,34 @@
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="dismiss()"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="campaignModalLabel">New Campaign</h4>
</div>
<div class="modal-body">
<div class="modal-body" id="modal_body">
<div class="row" id="modal.flashes"></div>
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" id="name" placeholder="Campaign name" autofocus>
<label class="control-label" for="template">Email Template:</label>
<input type="text" class="typeahead form-control" placeholder="Template Name" id="template"/>
<br>
<select class="form-control" placeholder="Template Name" id="template"/>
<option></option>
</select>
<label class="control-label" for="page">Landing Page:</label>
<input type="text" class="typeahead form-control" placeholder="Landing Page" id="page"/>
<br>
<select class="form-control" placeholder="Landing Page" id="page"/>
<option></option>
</select>
<label class="control-label" for="url">URL: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Location of gophish listener (must be reachable by targets!)"></i></label>
<input type="text" class="form-control" placeholder="http://192.168.1.1" id="url"/>
<label class="control-label" for="url">Schedule: </label>
<input type="text" class="form-control" id="launch_date"/>
<label class="control-label" for="profile">Sending Profile:</label>
<div class="input-group">
<input type="text" class="typeahead form-control" placeholder="Sending Profile" id="profile"/>
<select class="form-control" placeholder="Sending Profile" id="profile"/>
<option></option>
</select>
<span class="input-group-btn">
<button type="button" data-toggle="modal" data-target="#sendTestEmailModal" class="btn btn-primary typeahead-button"><i class="fa fa-envelope"></i> Send Test Email</button>
<button type="button" data-toggle="modal" data-target="#sendTestEmailModal" class="btn btn-primary button"><i class="fa fa-envelope"></i> Send Test Email</button>
</span>
</div>
<label class="control-label" for="users">Groups:</label>
<form id="groupForm">
<div class="input-group">
<input type="text" class="typeahead form-control" placeholder="Group Name" id="groupSelect" />
<span class="input-group-btn">
<button class="btn btn-primary typeahead-button"><i class="fa fa-plus"></i> Add</button>
</span>
</div>
</form>
<br />
<table id="groupTable" class="table table-hover table-striped table-condensed">
<thead>
<th>Group Name</th>
<th class="no-sort"></th>
<tbody>
</tbody>
</table>
<select class="form-control" id="users" multiple="multiple"></select>
</div>
</div>
<div class="modal-footer">