mirror of https://github.com/gophish/gophish
Moved campaign attributes to use Select2 instead of Typeahead.js
parent
ea7bb04156
commit
ed980a0861
|
@ -41,13 +41,12 @@ gulp.task('build', function() {
|
||||||
vendor_directory + 'jquery.dataTables.min.js',
|
vendor_directory + 'jquery.dataTables.min.js',
|
||||||
vendor_directory + 'dataTables.bootstrap.js',
|
vendor_directory + 'dataTables.bootstrap.js',
|
||||||
vendor_directory + 'datetime-moment.js',
|
vendor_directory + 'datetime-moment.js',
|
||||||
vendor_directory + 'hogan.js',
|
|
||||||
vendor_directory + 'jquery.ui.widget.js',
|
vendor_directory + 'jquery.ui.widget.js',
|
||||||
vendor_directory + 'jquery.fileupload.js',
|
vendor_directory + 'jquery.fileupload.js',
|
||||||
vendor_directory + 'jquery.iframe-transport.js',
|
vendor_directory + 'jquery.iframe-transport.js',
|
||||||
vendor_directory + 'sweetalert2.min.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(concat('vendor.js'))
|
||||||
.pipe(rename({
|
.pipe(rename({
|
||||||
|
@ -74,7 +73,9 @@ gulp.task('build', function() {
|
||||||
css_directory + 'chartist.min.css',
|
css_directory + 'chartist.min.css',
|
||||||
css_directory + 'bootstrap-datetime.css',
|
css_directory + 'bootstrap-datetime.css',
|
||||||
css_directory + 'checkbox.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(cleanCSS({compatibilty: 'ie9'}))
|
||||||
.pipe(concat('gophish.css'))
|
.pipe(concat('gophish.css'))
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -531,3 +531,13 @@ table.dataTable{
|
||||||
.btn-blue:hover{
|
.btn-blue:hover{
|
||||||
background-color:#64a1d6;
|
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;
|
||||||
|
}
|
||||||
|
|
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
File diff suppressed because one or more lines are too long
|
@ -27,22 +27,21 @@ function launch() {
|
||||||
preConfirm: function() {
|
preConfirm: function() {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
groups = []
|
groups = []
|
||||||
$.each($("#groupTable").DataTable().rows().data(), function(i, group) {
|
$("#users").select2("data").forEach(function(group){
|
||||||
groups.push({
|
groups.push({name: group.text});
|
||||||
name: unescapeHtml(group[0])
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
// Validate our fields
|
||||||
campaign = {
|
campaign = {
|
||||||
name: $("#name").val(),
|
name: $("#name").val(),
|
||||||
template: {
|
template: {
|
||||||
name: $("#template").val()
|
name: $("#template").select2("data")[0].text
|
||||||
},
|
},
|
||||||
url: $("#url").val(),
|
url: $("#url").val(),
|
||||||
page: {
|
page: {
|
||||||
name: $("#page").val()
|
name: $("#page").select2("data")[0].text
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
name: $("#profile").val()
|
name: $("#profile").select2("data")[0].text
|
||||||
},
|
},
|
||||||
launch_date: moment($("#launch_date").val(), "MM/DD/YYYY hh:mm a").format(),
|
launch_date: moment($("#launch_date").val(), "MM/DD/YYYY hh:mm a").format(),
|
||||||
groups: groups
|
groups: groups
|
||||||
|
@ -76,7 +75,7 @@ function launch() {
|
||||||
function sendTestEmail() {
|
function sendTestEmail() {
|
||||||
var test_email_request = {
|
var test_email_request = {
|
||||||
template: {
|
template: {
|
||||||
name: $("#template").val()
|
name: $("#template").select2("data")[0].text
|
||||||
},
|
},
|
||||||
first_name: $("input[name=to_first_name]").val(),
|
first_name: $("input[name=to_first_name]").val(),
|
||||||
last_name: $("input[name=to_last_name]").val(),
|
last_name: $("input[name=to_last_name]").val(),
|
||||||
|
@ -84,10 +83,10 @@ function sendTestEmail() {
|
||||||
position: $("input[name=to_position]").val(),
|
position: $("input[name=to_position]").val(),
|
||||||
url: $("#url").val(),
|
url: $("#url").val(),
|
||||||
page: {
|
page: {
|
||||||
name: $("#page").val()
|
name: $("#page").select2("data")[0].text
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
name: $("#profile").val()
|
name: $("#profile").select2("data")[0].text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
btnHtml = $("#sendTestModalSubmit").html()
|
btnHtml = $("#sendTestModalSubmit").html()
|
||||||
|
@ -107,15 +106,14 @@ function sendTestEmail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismiss() {
|
function dismiss() {
|
||||||
$("#modal\\.flashes").empty()
|
$("#modal\\.flashes").empty();
|
||||||
$("#name").val("")
|
$("#name").val("");
|
||||||
$("#template").val("")
|
$("#template").val("").change();
|
||||||
$("#page").val("")
|
$("#page").val("").change();
|
||||||
$("#url").val("")
|
$("#url").val("");
|
||||||
$("#profile").val("")
|
$("#profile").val("").change();
|
||||||
$("#groupSelect").val("")
|
$("#users").val("").change();
|
||||||
$("#modal").modal('hide')
|
$("#modal").modal('hide');
|
||||||
$("#groupTable").dataTable().DataTable().clear().draw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteCampaign(idx) {
|
function deleteCampaign(idx) {
|
||||||
|
@ -152,100 +150,104 @@ function deleteCampaign(idx) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function edit(campaign) {
|
function setupOptions() {
|
||||||
// Clear the bloodhound instance
|
|
||||||
group_bh.clear();
|
|
||||||
template_bh.clear();
|
|
||||||
page_bh.clear();
|
|
||||||
profile_bh.clear();
|
|
||||||
if (campaign == "new") {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function copy(idx) {
|
|
||||||
group_bh.clear();
|
|
||||||
template_bh.clear();
|
|
||||||
page_bh.clear();
|
|
||||||
profile_bh.clear();
|
|
||||||
api.groups.get()
|
api.groups.get()
|
||||||
.success(function(groups) {
|
.success(function(groups) {
|
||||||
if (groups.length == 0) {
|
if (groups.length == 0) {
|
||||||
modalError("No groups found!")
|
modalError("No groups found!")
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} 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()
|
api.templates.get()
|
||||||
.success(function(templates) {
|
.success(function(templates) {
|
||||||
if (templates.length == 0) {
|
if (templates.length == 0) {
|
||||||
modalError("No templates found!")
|
modalError("No templates found!")
|
||||||
return false
|
return false
|
||||||
} else {
|
} 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()
|
api.pages.get()
|
||||||
.success(function(pages) {
|
.success(function(pages) {
|
||||||
if (pages.length == 0) {
|
if (pages.length == 0) {
|
||||||
modalError("No pages found!")
|
modalError("No pages found!")
|
||||||
return false
|
return false
|
||||||
} else {
|
} 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()
|
api.SMTP.get()
|
||||||
.success(function(profiles) {
|
.success(function(profiles) {
|
||||||
if (profiles.length == 0) {
|
if (profiles.length == 0) {
|
||||||
modalError("No profiles found!")
|
modalError("No profiles found!")
|
||||||
return false
|
return false
|
||||||
} else {
|
} 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) {
|
||||||
|
setupOptions();
|
||||||
// Set our initial values
|
// Set our initial values
|
||||||
api.campaignId.get(campaigns[idx].id)
|
api.campaignId.get(campaigns[idx].id)
|
||||||
.success(function(campaign) {
|
.success(function(campaign) {
|
||||||
$("#name").val("Copy of " + campaign.name)
|
$("#name").val("Copy of " + campaign.name)
|
||||||
$("#template").val(campaign.template.name)
|
if (!campaign.template.id) {
|
||||||
$("#page").val(campaign.page.name)
|
$("#template").select2({
|
||||||
$("#profile").val(campaign.smtp.name)
|
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)
|
$("#url").val(campaign.url)
|
||||||
})
|
})
|
||||||
.error(function(data) {
|
.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').css('z-index', 1039 + (10 * $('body').data('fv_open_modals')));
|
||||||
$('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack');
|
$('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack');
|
||||||
});
|
});
|
||||||
$.fn.modal.Constructor.prototype.enforceFocus = function() {
|
// Scrollbar fix - https://stackoverflow.com/questions/19305821/multiple-modals-overlay
|
||||||
$(document)
|
$(document).on('hidden.bs.modal', '.modal', function () {
|
||||||
.off('focusin.bs.modal') // guard against infinite focus loop
|
$('.modal:visible').length && $(document.body).addClass('modal-open');
|
||||||
.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));
|
|
||||||
};
|
|
||||||
$('#modal').on('hidden.bs.modal', function(event) {
|
$('#modal').on('hidden.bs.modal', function(event) {
|
||||||
dismiss()
|
dismiss()
|
||||||
});
|
});
|
||||||
|
@ -343,154 +335,8 @@ $(document).ready(function() {
|
||||||
$("#loading").hide()
|
$("#loading").hide()
|
||||||
errorFlash("Error fetching campaigns")
|
errorFlash("Error fetching campaigns")
|
||||||
})
|
})
|
||||||
$("#groupForm").submit(function() {
|
// Select2 Defaults
|
||||||
// Add row to group table.
|
$.fn.select2.defaults.set("width", "100%");
|
||||||
var newRow = groupTable.row.add([
|
$.fn.select2.defaults.set("dropdownParent", $("#modal_body"));
|
||||||
escapeHtml($("#groupSelect").val()),
|
$.fn.select2.defaults.set("theme", "bootstrap");
|
||||||
'<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)
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -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, '&')
|
|
||||||
.replace(rLt, '<')
|
|
||||||
.replace(rGt, '>')
|
|
||||||
.replace(rApos, ''')
|
|
||||||
.replace(rQuot, '"') :
|
|
||||||
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);
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -68,45 +68,34 @@
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="dismiss()"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="dismiss()"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="campaignModalLabel">New Campaign</h4>
|
<h4 class="modal-title" id="campaignModalLabel">New Campaign</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body" id="modal_body">
|
||||||
<div class="row" id="modal.flashes"></div>
|
<div class="row" id="modal.flashes"></div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name">Name:</label>
|
<label for="name">Name:</label>
|
||||||
<input type="text" class="form-control" id="name" placeholder="Campaign name" autofocus>
|
<input type="text" class="form-control" id="name" placeholder="Campaign name" autofocus>
|
||||||
<label class="control-label" for="template">Email Template:</label>
|
<label class="control-label" for="template">Email Template:</label>
|
||||||
<input type="text" class="typeahead form-control" placeholder="Template Name" id="template"/>
|
<select class="form-control" placeholder="Template Name" id="template"/>
|
||||||
<br>
|
<option></option>
|
||||||
|
</select>
|
||||||
<label class="control-label" for="page">Landing Page:</label>
|
<label class="control-label" for="page">Landing Page:</label>
|
||||||
<input type="text" class="typeahead form-control" placeholder="Landing Page" id="page"/>
|
<select class="form-control" placeholder="Landing Page" id="page"/>
|
||||||
<br>
|
<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>
|
<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"/>
|
<input type="text" class="form-control" placeholder="http://192.168.1.1" id="url"/>
|
||||||
<label class="control-label" for="url">Schedule: </label>
|
<label class="control-label" for="url">Schedule: </label>
|
||||||
<input type="text" class="form-control" id="launch_date"/>
|
<input type="text" class="form-control" id="launch_date"/>
|
||||||
<label class="control-label" for="profile">Sending Profile:</label>
|
<label class="control-label" for="profile">Sending Profile:</label>
|
||||||
<div class="input-group">
|
<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">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="control-label" for="users">Groups:</label>
|
<label class="control-label" for="users">Groups:</label>
|
||||||
<form id="groupForm">
|
<select class="form-control" id="users" multiple="multiple"></select>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
Loading…
Reference in New Issue