Godwin
9 years ago
74 changed files with 6710 additions and 776 deletions
After Width: | Height: | Size: 421 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 541 B |
After Width: | Height: | Size: 1007 B |
@ -0,0 +1,79 @@ |
|||
(function() { |
|||
var pens = {}; |
|||
|
|||
Array.prototype.forEach.call(document.querySelectorAll('.textarea'), function(editor) { |
|||
startEditing(editor); |
|||
}); |
|||
|
|||
function startEditing(editor) { |
|||
var name = editor.dataset.name; |
|||
pens[name] = new Pen({ |
|||
editor: editor, |
|||
class: 'pen', |
|||
textarea: '<textarea name="' + name + '"></textarea>', |
|||
list: ['p', 'h3', 'h4', 'blockquote', 'insertorderedlist', 'insertunorderedlist', 'bold', 'italic', 'underline', 'strikethrough', 'createlink', 'insertimage'], |
|||
title: { |
|||
'p': 'Paragraph', |
|||
'h3': 'Major Heading', |
|||
'h4': 'Minor Heading', |
|||
'blockquote': 'Quotation', |
|||
'insertorderedlist': 'Ordered List', |
|||
'insertunorderedlist': 'Unordered List', |
|||
'bold': 'Bold', |
|||
'italic': 'Italic', |
|||
'underline': 'Underline', |
|||
'strikethrough': 'Strikethrough', |
|||
'createlink': 'Link', |
|||
'insertimage': 'Image' |
|||
} |
|||
}); |
|||
} |
|||
|
|||
Array.prototype.forEach.call(document.querySelectorAll('form'), function(form) { |
|||
var shouldAllowAlert = false; |
|||
form.addEventListener('submit', function() { |
|||
if (shouldAllowAlert) { |
|||
return; |
|||
} |
|||
Array.prototype.forEach.call(document.querySelectorAll('.textarea'), function(editor) { |
|||
var name = editor.dataset.name; |
|||
var textarea = document.querySelector('textarea[name="' + name + '"]'); |
|||
if (!textarea) { |
|||
textarea = document.createElement('textarea'); |
|||
textarea.name = name; |
|||
textarea.style.display = 'none'; |
|||
form.appendChild(textarea); |
|||
} |
|||
textarea.value = editor.innerHTML; |
|||
pens[name].destroy(); |
|||
}); |
|||
}, false); |
|||
Array.prototype.forEach.call(form.querySelectorAll('button'), function(button) { |
|||
form.addEventListener('click', function(event) { |
|||
shouldAllowAlert = (event.target.value === 'cancel'); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
Array.prototype.forEach.call(document.querySelectorAll('.check-box-field .other input'), function(input) { |
|||
var checkbox = document.getElementById(input.parentElement.parentElement.attributes.for.value); |
|||
input.addEventListener('keyup', function(event) { |
|||
if (event.target.value) { |
|||
checkbox.checked = true; |
|||
} |
|||
}); |
|||
input.addEventListener('click', function(event) { |
|||
checkbox.checked = true; |
|||
}); |
|||
var setRequired = function() { |
|||
if (checkbox.checked) { |
|||
input.setAttribute('required', 'required'); |
|||
} else { |
|||
input.removeAttribute('required'); |
|||
} |
|||
}; |
|||
Array.prototype.forEach.call(document.querySelectorAll('.check-box-field input'), function(_input) { |
|||
_input.addEventListener('change', function(event) { setRequired(); }); |
|||
}); |
|||
}); |
|||
})(); |
@ -1,37 +0,0 @@ |
|||
#= require froala_editor.min.js |
|||
$ -> |
|||
$('[data-editable]').editable({inlineMode: true, blockTags: ["n", "p", "h2", "blockquote", "pre"], buttons: ["formatBlock", "bold", "italic", "underline", "insertOrderedList", "insertUnorderedList", "sep", "createLink", "insertImage", "insertVideo", "html", "undo", "redo"]}) |
|||
$('[data-editor]').editable({inlineMode: false, blockTags: ["n", "p", "h2", "blockquote", "pre"], buttons: ["formatBlock", "bold", "italic", "underline", "insertOrderedList", "insertUnorderedList", "sep", "createLink", "html", "undo", "redo"]}) |
|||
$('.field.country-select-field select').change () -> |
|||
$country = $(this) |
|||
country = $country.val() |
|||
$territory = $('.field.subregion-select-field select') |
|||
if $territory.data().country == country |
|||
$territory.removeClass('can cant').addClass('can') |
|||
return |
|||
|
|||
$.post '/location/territories', {country: country}, |
|||
(json) -> |
|||
$territory.html('') |
|||
if json && Object.keys(json).length |
|||
$.each json, (code, name) -> |
|||
$territory.append($('<option>').text(name).attr('value', code)) |
|||
return |
|||
$territory.removeClass('can cant').addClass('can') |
|||
$territory.data().country = country |
|||
else |
|||
$territory.removeClass('can cant').addClass('cant') |
|||
return |
|||
, 'json' |
|||
$('img + input[type="file"]').change () -> |
|||
readURL(this); |
|||
return |
|||
|
|||
readURL = (input) -> |
|||
reader = null |
|||
if input.files && input.files[0] |
|||
reader = new FileReader() |
|||
reader.readAsDataURL input.files[0] |
|||
reader.onload = (e) -> |
|||
$(input).prev().attr('src', e.target.result) |
|||
return |
@ -0,0 +1,16 @@ |
|||
(function() { |
|||
Array.prototype.forEach.call(document.querySelectorAll('.number-field,.email-field,.text-field'), function(field) { |
|||
var input = field.querySelector('input'); |
|||
var positionLabel = function(input) { |
|||
field.classList[input.value ? 'remove' : 'add']('empty'); |
|||
} |
|||
positionLabel(input); |
|||
input.addEventListener('keyup', function(event) { positionLabel(event.target); }); |
|||
input.addEventListener('blur', function(event) { field.classList.remove('focused'); }); |
|||
input.addEventListener('focus', function(event) { field.classList.add('focused'); }); |
|||
}); |
|||
var errorField = document.querySelector('.input-field.has-error input, .input-field.has-error textarea'); |
|||
if (errorField) { |
|||
errorField.focus(); |
|||
} |
|||
})(); |
File diff suppressed because it is too large
@ -0,0 +1,212 @@ |
|||
$bumbleberry-no-markup: true; |
|||
|
|||
@import "bumbleberry"; |
|||
@import "settings"; |
|||
|
|||
$white: #FFF !default; |
|||
$black: #333 !default; |
|||
|
|||
.pen-icon { |
|||
font-size: 16px; |
|||
line-height: 40px; |
|||
min-width: 20px; |
|||
font-style: normal; |
|||
display: block; |
|||
float: left; |
|||
padding: 0 10px; |
|||
height: 2.2em; |
|||
overflow: hidden; |
|||
color: $white; |
|||
text-align: center; |
|||
cursor: pointer; |
|||
@include _(user-select, none); |
|||
} |
|||
|
|||
.pen { |
|||
position: relative; |
|||
|
|||
&:focus { |
|||
outline: none; |
|||
} |
|||
} |
|||
|
|||
.pen-menu, .pen-input { |
|||
font-size: 14px; |
|||
line-height:1; |
|||
} |
|||
|
|||
.pen-input { |
|||
display :none; |
|||
position: absolute; |
|||
width: 100%; |
|||
left: 0; |
|||
top: 0; |
|||
height: 2.6em; |
|||
line-height: 20px; |
|||
background-color: $black; |
|||
color: $white; |
|||
border: none; |
|||
text-align: center; |
|||
|
|||
&:focus { |
|||
outline: none; |
|||
} |
|||
} |
|||
|
|||
.pen-textarea { |
|||
display: block; |
|||
background: $white; |
|||
padding: 20px; |
|||
} |
|||
.pen textarea { |
|||
font-size: 14px; |
|||
border: none; |
|||
background: none; |
|||
width: 100%; |
|||
_height: 200px; |
|||
min-height: 200px; |
|||
resize: none; |
|||
} |
|||
|
|||
.pen-menu-below { |
|||
@include after { |
|||
top: -11px; |
|||
display: block; |
|||
@include _(transform, rotate(180deg)); |
|||
} |
|||
} |
|||
|
|||
.pen-menu { |
|||
white-space: nowrap; |
|||
box-shadow:1px 2px 3px -2px #222; |
|||
background: #333; |
|||
background-image: linear-gradient(to bottom, #222, #333); |
|||
opacity: 0.9; |
|||
position: fixed; |
|||
height: 5.2em; |
|||
border: 1px solid #333; |
|||
border-radius: 0.5em; |
|||
display:none; |
|||
z-index: 1000; |
|||
|
|||
@include after { |
|||
content: " "; |
|||
top: 100%; |
|||
border: solid transparent; |
|||
height: 0; |
|||
width: 0; |
|||
position: absolute; |
|||
pointer-events: none; |
|||
border-color: rgba(51, 51, 51, 0); |
|||
border-top-color: #333; |
|||
border-width: 6px; |
|||
left: 50%; |
|||
margin-left: -6px; |
|||
} |
|||
|
|||
[class^="icon-"], [class*=" icon-"] { |
|||
@include before { |
|||
speak: none; |
|||
display: inline-block; |
|||
width: 1em; |
|||
margin-right: .2em; |
|||
text-align: center; |
|||
font-variant: normal; |
|||
text-transform: none; |
|||
line-height: 1em; |
|||
margin-left: .2em; |
|||
vertical-align: text-top; |
|||
} |
|||
} |
|||
|
|||
.pen-icon { |
|||
&:hover { |
|||
background:#000; |
|||
} |
|||
&.active { |
|||
background: #000; |
|||
@include _(box-shadow, #{inset 2px 2px 4px #000, 0 0 0.25em #000}); |
|||
} |
|||
} |
|||
|
|||
.icon-h1, |
|||
.icon-h2, |
|||
.icon-h3, |
|||
.icon-h4, |
|||
.icon-h5, |
|||
.icon-h6 { |
|||
@include before { @include font-family(secondary); } |
|||
} |
|||
.icon-h1 { |
|||
@include before { content: 'H1'; } |
|||
} |
|||
.icon-h2 { |
|||
@include before { content: 'H2'; } |
|||
} |
|||
.icon-h3 { |
|||
@include before { content: 'H1'; } |
|||
} |
|||
.icon-h4 { |
|||
@include before { content: 'H2'; } |
|||
} |
|||
.icon-h5 { |
|||
@include before { content: 'H5'; } |
|||
} |
|||
.icon-h6 { |
|||
@include before { content: 'H6'; } |
|||
} |
|||
.icon-bold { |
|||
border-bottom-left-radius: 0.5em; |
|||
clear: left; |
|||
@include before { content: 'B'; font-weight: bold; } |
|||
} |
|||
.icon-italic { |
|||
@include before { content: 'I'; font-style: italic;; } |
|||
} |
|||
.icon-underline { |
|||
@include before { content: 'U'; text-decoration: underline; } |
|||
} |
|||
.icon-p { |
|||
@include font-family(secondary); |
|||
@include before { content: '¶'; } |
|||
} |
|||
.icon-blockquote { |
|||
@include before { |
|||
background-image: inline-svg-image('quote.svg'); |
|||
} |
|||
} |
|||
.icon-insertorderedlist { |
|||
border-top-right-radius: 0.5em; |
|||
|
|||
@include before { |
|||
background-image: inline-svg-image('ol.svg'); |
|||
} |
|||
} |
|||
.icon-insertunorderedlist { @include before { |
|||
background-image: inline-svg-image('ul.svg'); |
|||
} } |
|||
|
|||
.icon-strikethrough { @include before { content: 'S'; text-decoration: line-through; } } |
|||
|
|||
.icon-insertunorderedlist, |
|||
.icon-insertorderedlist, |
|||
.icon-blockquote, |
|||
.icon-insertimage, |
|||
.icon-createlink { |
|||
@include before { |
|||
content: ''; |
|||
background-size: contain; |
|||
height: 1em; |
|||
} |
|||
} |
|||
.icon-createlink { |
|||
@include before { |
|||
background-image: inline-svg-image('link.svg'); |
|||
} |
|||
} |
|||
.icon-insertimage { |
|||
@include before { |
|||
background-image: inline-svg-image('img.svg'); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,183 @@ |
|||
@import "settings"; |
|||
|
|||
body { |
|||
width: 100% !important; |
|||
-webkit-text-size-adjust: 100%; |
|||
-ms-text-size-adjust: 100%; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
.ExternalClass { |
|||
width: 100%; |
|||
line-height: 100%; |
|||
|
|||
p, span, font, td, div { |
|||
line-height: 100%; |
|||
} |
|||
} |
|||
|
|||
#backgroundTable { |
|||
margin: 0; |
|||
padding: 0; |
|||
width: 100% !important; |
|||
line-height: 100% !important; |
|||
} |
|||
|
|||
img { |
|||
outline: none; |
|||
text-decoration: none; |
|||
-ms-interpolation-mode: bicubic; |
|||
|
|||
a & { |
|||
border: none; |
|||
} |
|||
} |
|||
|
|||
.image_fix { |
|||
display: block; |
|||
max-width: 100%; |
|||
} |
|||
|
|||
a { |
|||
color: $colour-1; |
|||
|
|||
#outlook & { |
|||
padding: 0; |
|||
} |
|||
|
|||
&:link { color: $colour-1; } |
|||
&:visited { color: $colour-1; } |
|||
&:hover { color: $colour-5; } |
|||
|
|||
@media only screen and (max-device-width: 480px) { |
|||
&[href^="tel"], &[href^="sms"] { |
|||
text-decoration: none; |
|||
color: $black; |
|||
pointer-events: none; |
|||
cursor: default; |
|||
} |
|||
|
|||
.mobile_link &[href^="tel"], .mobile_link &[href^="sms"] { |
|||
text-decoration: default; |
|||
color: $colour-1 !important; |
|||
pointer-events: auto; |
|||
cursor: default; |
|||
} |
|||
} |
|||
} |
|||
|
|||
p { |
|||
margin: 1em; |
|||
color: $black !important; |
|||
} |
|||
|
|||
h1, h2, h3, h4, h5, h6 { |
|||
color: $black !important; |
|||
|
|||
a { |
|||
color: $colour-1 !important; |
|||
|
|||
&:active { |
|||
color: $colour-4 !important; |
|||
} |
|||
|
|||
&:visited { |
|||
color: $colour-2 !important; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
table { |
|||
border-collapse: collapse; |
|||
max-width: 512px; |
|||
min-width: 280px; |
|||
mso-table-lspace: 0pt; |
|||
mso-table-rspace: 0pt; |
|||
|
|||
td { |
|||
border-collapse: collapse; |
|||
} |
|||
} |
|||
|
|||
table#bb_full_width, |
|||
table#ecxbb_full_width { |
|||
|
|||
&, table { |
|||
max-width: none; |
|||
width: 100%; |
|||
} |
|||
} |
|||
|
|||
.diff, .ecxdiff { |
|||
margin: 1em 0 5em 1em; |
|||
overflow: auto; |
|||
|
|||
ul { |
|||
list-style: none; |
|||
padding: 0; |
|||
margin: 0; |
|||
position: relative; |
|||
} |
|||
|
|||
li { |
|||
width: 40%; |
|||
display: inline-block; |
|||
padding: 0.5em; |
|||
margin: 0; |
|||
background-color: lighten($colour-3, 33%); |
|||
border: 2px solid #DDD; |
|||
white-space: nowrap; |
|||
|
|||
&:nth-child(odd) { |
|||
margin-right: -3px; |
|||
float: left; |
|||
} |
|||
|
|||
&:nth-child(even) { |
|||
background-color: lighten($colour-5 , 33%); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Buttons |
|||
h3 { |
|||
b { |
|||
padding: 10px 20px; |
|||
line-height: 50px; |
|||
|
|||
a, a:visited { |
|||
color: #FFF !important; |
|||
background-color: $colour-5; |
|||
text-decoration: none !important; |
|||
border-radius: 4px; |
|||
padding: 10px 15px; |
|||
margin-left: 20px; |
|||
border-bottom: 3px solid darken($colour-5, 10%); |
|||
-webkit-box-shadow: 0 0.5em 1.5em -0.75em #000; |
|||
box-shadow: 0 0.5em 1.5em -0.75em #000; |
|||
cursor: pointer !important; |
|||
} |
|||
|
|||
a:hover { |
|||
background-color: darken($colour-5, 10%); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { |
|||
a[href^="tel"], a[href^="sms"] { |
|||
text-decoration: none; |
|||
color: $colour-1; |
|||
pointer-events: none; |
|||
cursor: default; |
|||
|
|||
.mobile_link & { |
|||
text-decoration: default; |
|||
color: $colour-1 !important; |
|||
pointer-events: auto; |
|||
cursor: default; |
|||
} |
|||
} |
|||
} |
@ -1,5 +1,11 @@ |
|||
class ConferenceRegistration < ActiveRecord::Base |
|||
has_many :conference_registration_responses |
|||
|
|||
AttendingOptions = [:yes, :no] |
|||
end |
|||
class ConferenceRegistration < ActiveRecord::Base |
|||
belongs_to :conference |
|||
belongs_to :user |
|||
has_many :conference_registration_responses |
|||
|
|||
AttendingOptions = [:yes, :no] |
|||
|
|||
def languages |
|||
user.languages |
|||
end |
|||
end |
|||
|
@ -1,7 +1,9 @@ |
|||
= render :partial => 'application/header', :locals => {:image_file => @banner_image || '403.jpg'} |
|||
%article |
|||
- if @template |
|||
- if @template.present? |
|||
=render @template |
|||
- else |
|||
%h2=_'articles.permission_denied.headings.main','Sorry, you currently don\'t have access to this page' |
|||
%p=_'articles.permission_denied.paragraphs.main', :p |
|||
= row do |
|||
= columns do |
|||
%h2=_'articles.permission_denied.headings.main','Sorry, you currently don\'t have access to this page' |
|||
%p=_'articles.permission_denied.paragraphs.main', :p |
|||
|
@ -1,6 +1,15 @@ |
|||
%article |
|||
= columns(medium: 12) do |
|||
%h2=_'articles.conference_registration.headings.Confirm_You_Email_Address','Confirm Email Address' |
|||
= columns(medium: 6, large: 5) do |
|||
= form_tag register_path(@this_conference.slug) do |
|||
%h2=_'articles.conference_registration.headings.Pre_Registration_Details','Pre-Registration is now open!' |
|||
%p=_'articles.conference_registration.paragraphs.Pre_Registration_Details', 'By completing the pre-registration process you are letting us know that you are interested in coming, it allows us to contact you when we have news, and lets us better plan by knowing who might be coming. Once registration is fully open, we will need to ask a few more questions and you can confirm whether or not you will actually be coming.' |
|||
%h3=_'articles.conference_registration.headings.Verify_Account','Verify your account' |
|||
%p=_'articles.conference_registration.paragraphs.Verify_Account', 'In order to confirm that you are a real person and that we will be able to contact you later, please verify your email address. Before continuing with pre-registration, we will send you an email that will allow you to continue with the process.' |
|||
= columns(medium: 12) do |
|||
= form_tag register_path(@this_conference.slug), class: 'flex-form' do |
|||
.email-field.input-field.big |
|||
= email_field_tag :email, nil, required: true |
|||
= label_tag :email |
|||
= button_tag :continue, :value => :confirm_email |
|||
= columns(medium: 12, class: 'flex-column') do |
|||
%p.stretch-item=_'articles.conference_registration.paragraphs.facebook_sign_in','Alternatively you can sign in using your Facebook account and skip waiting for us to send you an email.' |
|||
= link_to (_'forms.actions.generic.facebook_sign_in','Facebook Sign In'), auth_at_provider_path(:provider => :facebook), class: [:button, :facebook] |
|||
|
@ -0,0 +1,26 @@ |
|||
= columns(medium: 12) do |
|||
%h2=_(@page_title) |
|||
%p=_'articles.conference_registration.paragraphs.Contact_Info', :s, 2 |
|||
= columns(medium: 12) do |
|||
= form_tag register_path(@this_conference.slug) do |
|||
%h3=_'articles.conference_registration.headings.name','What is your name?' |
|||
.text-field.input-field{class: @errors[:name].present? ? 'has-error' : nil} |
|||
= show_errors :name |
|||
= label_tag :name |
|||
= text_field_tag :name, @name, required: true |
|||
%h3=_'articles.conference_registration.headings.location','Where are you coming from?' |
|||
.text-field.input-field{class: @errors[:location].present? ? 'has-error' : nil} |
|||
= show_errors :location |
|||
= label_tag :location |
|||
= text_field_tag :location, @registration.city || location(lookup_ip_location), required: true |
|||
%h3=_'articles.conference_registration.headings.languages','Which languages do you speak?' |
|||
- puts @errors.to_json.to_s |
|||
.check-box-field.input-field{class: @errors[:languages].present? ? 'has-error' : nil} |
|||
- [:en, :es, :fr].each do |language| |
|||
= check_box_tag "languages[#{language}]", 1, (@registration.languages || [I18n.locale.to_s]).include?(language.to_s) |
|||
= label_tag "languages_#{language}" do |
|||
= _"languages.#{language}" |
|||
= show_errors :languages |
|||
.actions.next-prev |
|||
= button_tag (params[:step] == :save ? :save : :next), value: :contact_info |
|||
= button_tag :previous, value: :prev_contact_info, class: :subdued, formnovalidate: true |
@ -1,10 +1,9 @@ |
|||
%article |
|||
= columns(medium: 12) do |
|||
%h2=_'articles.conference_registration.headings.Policy_Agreement' |
|||
%p=_'articles.conference_registration.paragraphs.Policy_Agreement', :s, 2 |
|||
= columns(medium: 10, push: 1) do |
|||
= render 'application/policy' |
|||
= columns(medium: 12) do |
|||
%p=_'articles.conference_registration.paragraphs.Confirm_Agreement', :p |
|||
= form_tag register_path(@this_conference.slug) do |
|||
.actions= button_tag :agree, :value => :policy |
|||
= columns(medium: 12) do |
|||
%h2=_@page_title |
|||
%p=_'articles.conference_registration.paragraphs.Policy_Agreement', :s, 2 |
|||
= columns(medium: 10, push: 1) do |
|||
= render 'application/policy' |
|||
= columns(medium: 12) do |
|||
%p=_'articles.conference_registration.paragraphs.Confirm_Agreement', :p |
|||
= form_tag register_path(@this_conference.slug) do |
|||
.actions= button_tag :agree, :value => :policy |
|||
|
@ -1,42 +1,20 @@ |
|||
= columns(medium: 12) do |
|||
%h2=_'articles.conference_registration.headings.Workshops','Workshops' |
|||
= columns(medium: 6) do |
|||
%h3=_'articles.conference_registration.headings.Add_Workshop' |
|||
= form_tag register_path(@this_conference.slug) do |
|||
.text-field.input-field |
|||
= text_field_tag :title, nil, required: true |
|||
= label_tag :title |
|||
= text_area_tag :info, nil, required: true |
|||
= button_tag :add |
|||
= columns(medium: 6) do |
|||
%h3=_'articles.conference_registration.headings.Your_Workshops' |
|||
%ul.workshops.my-workshops |
|||
- @my_workshops.each do |workshop| |
|||
%li |
|||
%h4=workshop[:title] |
|||
.info |
|||
%h5{:contenteditable => true}=workshop[:title] |
|||
%p{:contenteditable => true}=workshop[:info] |
|||
= form_tag register_path(@this_conference.slug) do |
|||
= button_tag :save, :value => :save |
|||
= button_tag :delete, :value => :delete |
|||
= button_tag :cancel, :value => :cancel |
|||
%h2=_@page_title |
|||
= columns(medium: 12) do |
|||
= form_tag register_path(@this_conference.slug) do |
|||
= button_tag :previous, :value => :registration |
|||
= button_tag :next, :value => :workshops |
|||
:javascript |
|||
function makeWorkshopsClickable() { |
|||
var workshops = document.querySelectorAll('ul.workshops h4'); |
|||
for (var i = 0; i < workshops.length; i++) { |
|||
workshops[i].addEventListener('click', function(e) { |
|||
var workshop = e.target.parentElement; |
|||
workshop.className = 'view'; |
|||
workshop.querySelector('form').onsubmit = function() { |
|||
workshop.removeAttribute('class'); |
|||
return false; |
|||
} |
|||
}, false); |
|||
} |
|||
} |
|||
makeWorkshopsClickable(); |
|||
- if @my_workshops.present? |
|||
%h3=_'articles.conference_registration.headings.Your_Workshops' |
|||
%p=_'articles.conference_registration.paragraphs.Your_Workshops', :p |
|||
= render 'workshops/workshop_previews', :workshops => @my_workshops |
|||
- else |
|||
%p=_'articles.conference_registration.paragraphs.Create_Workshop', :p |
|||
= link_to (_'articles.conference_registration.headings.Add_Workshop'), create_workshop_path(@this_conference.slug), class: :button |
|||
- if @workshops_in_need.present? |
|||
= columns(medium: 12) do |
|||
%h3=_'articles.conference_registration.headings.Workshops_Looking_For_Facilitators' |
|||
%p=_'articles.conference_registration.paragraphs.Workshops_Looking_For_Facilitators', :p |
|||
= render 'workshops/workshop_previews', :workshops => @workshops_in_need |
|||
- if @workshops.present? |
|||
= columns(medium: 12) do |
|||
%h3=_'articles.conference_registration.headings.All_Workshops' |
|||
-#%p=_'articles.conference_registration.paragraphs.All_Workshops', :p |
|||
= render 'workshops/workshop_previews', :workshops => @workshops |
|||
|
@ -1,4 +1,20 @@ |
|||
= render :partial => 'page_header', :locals => {:page_key => 'Conference_Registration'} |
|||
- if (steps = current_registration_steps) |
|||
= row id: 'registration-steps', class: 'flow-steps' do |
|||
= columns do |
|||
%ul |
|||
- current_registration_steps.each do | step | |
|||
- text = _"articles.conference_registration.headings.#{step[:name].to_s}" |
|||
%li{class: [step[:enabled] ? :enabled : nil, @register_template == step[:name] ? :current : nil]} |
|||
- if step[:enabled] |
|||
.step= link_to text, register_step_path(@this_conference.slug, step[:name]) |
|||
- else |
|||
.step= text |
|||
- if @warnings.present? |
|||
= row class: 'warnings', tag: :ul do |
|||
- @warnings.each do | warning | |
|||
= columns tag: :li do |
|||
= warning |
|||
%article |
|||
= row do |
|||
= render (@register_template || :register).to_s |
|||
= render @register_template.to_s |
|||
|
@ -1,14 +1,23 @@ |
|||
%nav |
|||
#main-nav |
|||
= row(class: 'inner-nav') do |
|||
- if logged_in? |
|||
= columns(medium: 12, class: 'user-nav') do |
|||
.user-nav-bar |
|||
=link_to (_!current_user.name), register_step_path(@conference.slug, :contact_info), class: 'my-account' |
|||
=link_to (_'forms.actions.generic.Log_out'), logout_path |
|||
= columns(medium: 4, class: 'inner-nav') do |
|||
= link_to '/', :class => 'logo' do |
|||
= link_to home_path, :class => 'logo' do |
|||
= svg_sprite('icons', "bb-icon-logo", "Logo") |
|||
= svg_sprite('icons', "bb-icon-logo-text", "Logo Text") |
|||
= off_screen _!'Bike Bike' |
|||
= columns(medium: 8, class: 'nav') do |
|||
= nav_link about_path, (_'page_titles.About'), 'about' |
|||
= nav_link policy_path, (_'page_titles.Policy'), 'policy' |
|||
- if @conference && @conference.registration_open |
|||
= nav_link register_path(@conference.slug), (_'page_titles.Register') |
|||
= nav_link :about |
|||
= nav_link :policy |
|||
- case (@conference ? @conference.registration_status.to_sym : :closed) |
|||
- when :pre |
|||
= nav_link register_path(@conference.slug), :pre_register, :register |
|||
- when :open |
|||
= nav_link register_path(@conference.slug), :register |
|||
- else |
|||
= render 'shared/donate_button', :email_address => (@conference.paypal_email_address || @conference.email_address) |
|||
= render 'shared/donate_button', :email_address => (@conference.paypal_email_address || @conference.email_address || @conference.organizations.first.email_address) |
@ -1,4 +1,8 @@ |
|||
%p=_'email.general.paragraph.thank_you', "Thank you #{@user.firstname},", :vars => {:name => @user.firstname} |
|||
%p=_'email.registration.paragraph.confirmed', "You have successfully registered for #{@conference.title}. You can modify your registration details, pay, or add workshops at any time by restarting the registration process. If you have yet to pay or add your workshops and plan to do so, we ask that you take care of it as soon as possible to help us prepare in advance of your arrival.", :vars => {:conference_title => @conference.title} |
|||
|
|||
%p=_'email.general.paragraph.see_you', "See you in #{@conference.location.city}!", :vars => {:conference_location => @conference.location.city} |
|||
- if @conference.registration_status.to_sym == :pre |
|||
%p=_'email.general.paragraph.pre_registered', "You have successfully pre-registered for #{@conference.title}. We will let you know when registration is fully open and if there is any important news about the conference that you should know about. We encourage you to create or volunteer to facilitate workshops soon. You can do that, or change your registration details at any time by clicking on the pre-register link again.", :vars => {:conference_title => @conference.title} |
|||
- else |
|||
%p=_'email.registration.paragraph.confirmed', :vars => {:conference_title => @conference.title} |
|||
|
|||
%p=_'email.general.paragraph.see_you', :vars => {:conference_location => @conference.location.city} |
|||
|
@ -1,5 +1,8 @@ |
|||
=_'email.general.paragraph.thank_you', "Thank you #{@user.firstname},", :vars => {:name => @user.firstname} |
|||
|
|||
=_'email.registration.paragraph.confirmed', "You have successfully registered for #{@conference.title}. You can modify your registration details, pay, or add workshops at any time by restarting the registration process. If you have yet to pay or add your workshops and plan to do so, we ask that you take care of it as soon as possible to help us prepare in advance of your arrival.", :vars => {:conference_title => @conference.title} |
|||
- if @conference.registration_status.to_sym == :pre |
|||
=_'email.general.paragraph.pre_registered', "You have successfully pre-registered for #{@conference.title}. We will let you know when registration is fully open and if there is any important news about the conference that you should know about. We encourage you to create or volunteer to facilitate workshops soon. You can do that, or change your registration details at any time by clicking on the pre-register link again.", :vars => {:conference_title => @conference.title} |
|||
- else |
|||
=_'email.registration.paragraph.confirmed', :vars => {:conference_title => @conference.title} |
|||
|
|||
=_'email.general.paragraph.see_you', "See you in #{@conference.location.city}!", :vars => {:conference_location => @conference.location.city} |
|||
=_'email.general.paragraph.see_you', :vars => {:conference_location => @conference.location.city} |
@ -1 +0,0 @@ |
|||
Hi, this is a test. Did you get it? |
@ -0,0 +1,15 @@ |
|||
%p=_('email.translations.paragraph.workshop_original_content_changed', "#{@translator.name} has modified the original content for #{@workshop.title}. Translations may need to be updated.", vars: {user_name: @translator.firstname, workshop_title: @workshop.title}) |
|||
|
|||
- @data.each do |field, values| |
|||
%h2=_"forms.labels.generic.#{field.to_s}" |
|||
%b=_'email.translations.headings.new_value' |
|||
%blockquote=_!values[:new].html_safe |
|||
%b=_'email.translations.headings.old_value' |
|||
%blockquote=_!values[:old].html_safe |
|||
%b=_'email.translations.headings.diff' |
|||
=_!(values[:diff][:html].html_safe) |
|||
|
|||
%p |
|||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: " |
|||
- workshop_link = view_workshop_url(@workshop.conference.slug, @workshop.id) |
|||
%a{href: workshop_link}=workshop_link |
@ -0,0 +1,25 @@ |
|||
=_('email.translations.paragraph.workshop_translated', "#{@translator.name} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title}) |
|||
|
|||
|
|||
- @data.each do |field, values| |
|||
=(_!'** ') + (_"forms.labels.generic.#{field.to_s}") |
|||
=(_!' - ') + (_'email.translations.headings.new_value') |
|||
=(_!' ------------------------------ ') |
|||
=_!values[:new] |
|||
=(_!' ------------------------------ ') |
|||
|
|||
|
|||
=(_!' - ') + (_'email.translations.headings.old_value') |
|||
=(_!' ------------------------------ ') |
|||
=_!values[:old] |
|||
=(_!' ------------------------------ ') |
|||
|
|||
|
|||
=(_!' - ') + (_'email.translations.headings.diff') |
|||
=(_!' ------------------------------ ') |
|||
=(_!values[:diff][:text]) |
|||
=(_!' ------------------------------ ') |
|||
|
|||
|
|||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: " |
|||
= view_workshop_url(@workshop.conference.slug, @workshop.id) |
@ -0,0 +1,15 @@ |
|||
%p=_('email.translations.paragraph.workshop_translated', "#{@translator.name} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title}) |
|||
|
|||
- @data.each do |field, values| |
|||
%h2=_"forms.labels.generic.#{field.to_s}" |
|||
%b=_'email.translations.headings.new_value' |
|||
%blockquote=_!values[:new].html_safe |
|||
%b=_'email.translations.headings.old_value' |
|||
%blockquote=_!values[:old].html_safe |
|||
%b=_'email.translations.headings.diff' |
|||
=_!(values[:diff][:html].html_safe) |
|||
|
|||
%p |
|||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: " |
|||
- workshop_link = view_workshop_url(@workshop.conference.slug, @workshop.id) |
|||
%a{href: workshop_link}=workshop_link |
@ -0,0 +1,25 @@ |
|||
=_('email.translations.paragraph.workshop_translated', "#{@translator.firstname} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title}) |
|||
|
|||
|
|||
- @data.each do |field, values| |
|||
=(_!'** ') + (_"forms.labels.generic.#{field.to_s}") |
|||
=(_!' - ') + (_'email.translations.headings.new_value') |
|||
=(_!' ------------------------------ ') |
|||
=_!values[:new] |
|||
=(_!' ------------------------------ ') |
|||
|
|||
|
|||
=(_!' - ') + (_'email.translations.headings.old_value') |
|||
=(_!' ------------------------------ ') |
|||
=_!values[:old] |
|||
=(_!' ------------------------------ ') |
|||
|
|||
|
|||
=(_!' - ') + (_'email.translations.headings.diff') |
|||
=(_!' ------------------------------ ') |
|||
=(_!values[:diff][:text]) |
|||
=(_!' ------------------------------ ') |
|||
|
|||
|
|||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: " |
|||
= view_workshop_url(@workshop.conference.slug, @workshop.id) |
@ -1,8 +1,12 @@ |
|||
- # encoding: utf-8 |
|||
%ul.workshop-previews |
|||
%ul.workshop-list |
|||
- workshops.sort_by{ |w| w.title.downcase }.each do |w| |
|||
- if w.title |
|||
%li |
|||
=link_to view_workshop_path(@this_conference.slug, w.id) do |
|||
%h4=_!(w.title) |
|||
%p=_!(w.info || '').gsub(/\n\#+(.*?)\r/, "\n<strong>\\1</strong>\r").gsub(/[\t ]*\r\n[\t ]*/, '<br>').html_safe |
|||
- is_interested = w.interested?(current_user) |
|||
%li{class: [is_interested ? :interested : nil]} |
|||
= link_to view_workshop_path(w.conference.slug, w.id) do |
|||
%h4.title=w.title |
|||
.workshop-interest |
|||
- if is_interested |
|||
=_'articles.workshops.info.you_are_interested_count', "You and #{w.interested_count - 1} others are interested in this workshop", :vars => {:count => (w.interested_count - 1)} |
|||
- elsif w.interested_count > 0 |
|||
=_'articles.workshops.info.interested_count', "#{w.interested_count} people are interested in this workshop", :vars => {:count => w.interested_count} |
|||
.workshop-description=w.info.html_safe |
|||
|
@ -1,9 +1,11 @@ |
|||
= render 'conferences/page_header', :page_key => 'Facilitate_Workshop' |
|||
= render 'conferences/page_header', :page_key => 'Conference_Registration' |
|||
|
|||
%article |
|||
= row do |
|||
%h2=_'page_titles.conferences.Facilitate_Workshop' |
|||
= columns(medium: 12) do |
|||
=m('articles.workshops.paragraphs.facilitate_request_sent','Your request has been sent. You will receive an email once your request is approved or denied or if the current facilitators have any questions.') |
|||
= columns(medium: 12) do |
|||
.actions |
|||
= link_to (_'actions.workshops.View'), view_workshop_url(@this_conference.slug, @workshop.id), :class => 'button' |
|||
= link_to (_'actions.workshops.View_All'), workshops_url(@this_conference.slug), :class => 'button' |
|||
= link_to (_'actions.workshops.View_All'), register_step_path(@this_conference.slug, :wokshops), :class => 'button' |
|||
|
@ -1,13 +1,18 @@ |
|||
= render 'conferences/page_header', :page_key => 'Workshops' |
|||
|
|||
%article |
|||
= row do |
|||
= columns(medium: 12) do |
|||
%h2=_'articles.workshops.headings.Workshops' |
|||
= columns(medium: 6) do |
|||
= row do |
|||
= columns(medium: 12) do |
|||
%p=_'articles.workshops.paragraphs.Workshops', :p |
|||
= columns(medium: 6) do |
|||
= row do |
|||
= columns(medium: 12) do |
|||
%h3=_'articles.workshops.headings.Your_Workshops' |
|||
= render 'workshops/workshop_previews', :workshops => @my_workshops |
|||
= link_to (_'actions.workshops.create','New Workshop'), create_workshop_path(@this_conference.slug), class: [:button, :modify] if @conference.registration_open |
|||
= row do |
|||
= render 'workshops/workshop_previews', :workshops => @workshops |
|||
= columns(medium: 12) do |
|||
%h3=_'articles.workshops.headings.All_Workshops' |
|||
= render 'workshops/workshop_previews', :workshops => @workshops |
@ -1,69 +1,91 @@ |
|||
= render 'conferences/page_header', :page_key => (@workshop ? 'Edit_Workshop' : 'Create_Workshop') |
|||
- add_stylesheet :editor |
|||
- add_inline_script :pen |
|||
- add_inline_script :markdown |
|||
- add_inline_script :editor |
|||
|
|||
-#= render 'conferences/page_header', :page_key => (@workshop.id.present? ? 'Edit_Workshop' : 'Create_Workshop') |
|||
= render 'conferences/page_header', :page_key => 'Conference_Registration' |
|||
|
|||
%article |
|||
= row do |
|||
= form_tag save_workshop_path(@this_conference.slug), class: 'composition' do |
|||
= (hidden_field_tag :workshop_id, @workshop.id) if @workshop |
|||
= columns(medium: 12) do |
|||
=m('articles.workshops.paragraphs.new_workshop','Please accurately describe your workshop in detail. This will help hosts decide if they wish to add it to the schedule and when it should best be scheduled. Enter normal text but if you want to get fancy you can use [Markdown](http://daringfireball.net/projects/markdown/basics).') |
|||
- if @workshop.id.present? |
|||
= hidden_field_tag :workshop_id, @workshop.id |
|||
- if @is_translating |
|||
%h2=_(@page_title, vars: @page_title_vars) |
|||
= hidden_field_tag :translation, @translation |
|||
- else |
|||
%h2=_@page_title, :t |
|||
- else |
|||
%h2=_@page_title, :t |
|||
.text-field.input-field.big |
|||
= label_tag :title |
|||
= text_field_tag :title, @title, :required => true |
|||
- if @workshop && I18n.locale.to_s != @workshop.locale.to_s |
|||
= text_field_tag :title, @title, required: true, lang: @translation |
|||
- if @is_translating |
|||
.original-text |
|||
%h4=_'translate.content.Translation_of' |
|||
.value=@workshop.title! |
|||
.text-area-field.input-field |
|||
= label_tag :info |
|||
= text_area_tag :info, @info, :required => true |
|||
- if @workshop && I18n.locale.to_s != @workshop.locale.to_s |
|||
.input-field-help=_'articles.workshops.paragraphs.info', :s, 2 |
|||
.textarea#info{lang: @translation, data: {name: :info}}=(@info || '').html_safe |
|||
- if @is_translating |
|||
.original-text |
|||
%h4=_'translate.content.Translation_of' |
|||
.value=markdown @workshop.info! |
|||
- if !@workshop || @can_edit |
|||
.value=(@workshop.info! || '').html_safe |
|||
- if !@is_translating && (@workshop.id.blank? || @can_edit) |
|||
= columns(medium: 6) do |
|||
%h3=_'articles.workshops.headings.languages','Workshop Language' |
|||
- [:en, :es, :fr].each do |language| |
|||
.single-check-box-field.input-field |
|||
.input-field-help=_'articles.workshops.paragraphs.languages', :s, 2 |
|||
.check-box-field.vertical.input-field |
|||
- [:en, :es, :fr].each do |language| |
|||
= check_box_tag "languages[#{language}]", 1, @languages && @languages.include?(language) |
|||
= label_tag "languages_#{language}" do |
|||
= check_box_tag "languages[#{language}]", 1, @languages && @languages.include?(language) |
|||
= _"languages.#{language}" |
|||
%h3=_'articles.workshops.headings.theme','Theme' |
|||
.input-field-help=_'articles.workshops.paragraphs.theme', :p |
|||
.input-field-help=_'articles.workshops.paragraphs.theme', :s, 2 |
|||
- theme_found = false |
|||
- [:race_gender, :mechanics, :funding, :organization, :community].each do |theme| |
|||
.single-radio-button-field.input-field |
|||
.check-box-field.vertical.input-field |
|||
- Workshop.all_themes.each do |theme| |
|||
- is_selected = (@workshop.theme.to_s == theme.to_s) |
|||
- theme_found ||= is_selected |
|||
= radio_button_tag :theme, theme, is_selected |
|||
= label_tag "theme_#{theme}" do |
|||
- is_selected = (@theme == theme) |
|||
- theme_found ||= is_selected |
|||
= radio_button_tag :theme, theme, is_selected |
|||
= _"workshop.options.theme.#{theme}" |
|||
.single-radio-button-field.other-field.input-field |
|||
- is_other = (@workshop.theme.present? && !theme_found) |
|||
= radio_button_tag :theme, :other, is_other |
|||
= label_tag "theme_other" do |
|||
= radio_button_tag :theme, :other, (@theme && !theme_found) |
|||
%div |
|||
= _"workshop.options.theme.other" |
|||
= text_field_tag :other_theme, (@theme && !theme_found ? @theme : nil) |
|||
.other |
|||
= text_field_tag :other_theme, (is_other ? @workshop.theme : nil), placeholder: (_"workshop.options.theme.other"), required: is_other |
|||
= columns(medium: 6) do |
|||
%h3=_'articles.workshops.headings.needs','What do you need?' |
|||
- [:sound, :projector, :tools].each do |need| |
|||
.single-check-box-field.input-field |
|||
.input-field-help=_'articles.workshops.paragraphs.needs', :s, 2 |
|||
.check-box-field.vertical.input-field |
|||
- [:sound, :projector, :tools].each do |need| |
|||
= check_box_tag "needs[#{need.to_s}]", 1, JSON.parse(@workshop.needs || '[]').include?(need.to_s) |
|||
= label_tag "needs_#{need}" do |
|||
= check_box_tag "needs[#{need}]", 1, @needs.include?(need) |
|||
= _"workshop.options.needs.#{need}" |
|||
= _"workshop.options.needs.#{need.to_s}" |
|||
%h3=_'articles.workshops.headings.space','Type of space' |
|||
.input-field-help=_'articles.workshops.paragraphs.space', :p |
|||
- [:meeting_room, :workshop, :outdoor_meeting].each do |space| |
|||
.single-radio-button-field.input-field |
|||
.input-field-help=_'articles.workshops.paragraphs.space', :s, 2 |
|||
.check-box-field.vertical.input-field |
|||
- [:meeting_room, :workshop, :outdoor_meeting].each do |space| |
|||
= radio_button_tag :space, space, @workshop.space == space |
|||
= label_tag "space_#{space}" do |
|||
= radio_button_tag :space, space, @space == space |
|||
= _"workshop.options.space.#{space}" |
|||
%h3=_'articles.workshops.headings.needs_facilitators','Looking for help?' |
|||
.input-field-help=_'articles.workshops.paragraphs.needs_facilitators', :s, 2 |
|||
.check-box-field.vertical.input-field |
|||
= check_box_tag :needs_facilitators, 1, @workshop.needs_facilitators |
|||
= label_tag :needs_facilitators do |
|||
= _'workshop.options.needs_facilitators', 'Needs Additional Facilitators' |
|||
= columns(medium: 12) do |
|||
%h3=_'articles.workshops.headings.notes','Notes for Conference Organizers and Workshop Facilitators' |
|||
%p=_'articles.workshops.paragraphs.notes','Notes are only viewable by conference hosts and workshop facilitators' |
|||
.text-area-field.input-field |
|||
= label_tag :notes |
|||
= text_area_tag :notes, @notes |
|||
.warning-info=_'articles.workshops.paragraphs.notes','Notes are only viewable by conference hosts and workshop facilitators' |
|||
.textarea#notes{data: {name: :notes}}=(@workshop.notes || '').html_safe |
|||
= columns(medium: 12) do |
|||
.actions.right |
|||
= button_tag :save, :value => :save |
|||
.actions.next-prev |
|||
= button_tag :save, value: :save |
|||
= button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true |
|||
|
|||
|
@ -1,8 +1,20 @@ |
|||
= render 'conferences/page_header', :page_key => 'View_Workshop' |
|||
= render 'conferences/page_header', :page_key => 'Conference_Registration' |
|||
|
|||
= row id: 'registration-steps', class: 'flow-steps' do |
|||
= columns do |
|||
%ul |
|||
- current_registration_steps.each do | step | |
|||
- text = _"articles.conference_registration.headings.#{step[:name].to_s}" |
|||
%li{class: [step[:enabled] ? :enabled : nil, :workshops == step[:name] ? :current : nil]} |
|||
- if step[:enabled] |
|||
.step= link_to text, register_step_path(@this_conference.slug, step[:name]) |
|||
- else |
|||
.step= text |
|||
|
|||
%article |
|||
= render 'workshops/show', :workshop => @workshop, :preview => false |
|||
= render 'workshops/show', :workshop => @workshop, :translations_available_for_editing => @translations_available_for_editing, :preview => false |
|||
= row do |
|||
= columns(medium: 12) do |
|||
.actions.right |
|||
= (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), :class => 'button modify') if @workshop.can_edit?(current_user) |
|||
= (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), :class => [:button, :modify]) if @workshop.can_edit?(current_user) |
|||
= (link_to (_'actions.workshops.Delete'), delete_workshop_path(@this_conference.slug, @workshop.id), :class => 'button delete') if @workshop.can_delete?(current_user) |
|||
|
@ -0,0 +1,11 @@ |
|||
# Be sure to restart your server when you modify this file. |
|||
|
|||
# Version of your assets, change this if you want to expire all your assets. |
|||
Rails.application.config.assets.version = '1.0' |
|||
|
|||
# Add additional assets to the asset load path |
|||
# Rails.application.config.assets.paths << Emoji.images_path |
|||
|
|||
# Precompile additional assets. |
|||
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. |
|||
Rails.application.config.assets.precompile += %w( map.js pen.js editor.js markdown.js main.js favicon.ico ) |
@ -0,0 +1,9 @@ |
|||
class AddRegistrationStatusToConferences < ActiveRecord::Migration |
|||
def change |
|||
add_column :conferences, :registration_status, :string |
|||
Conference.find_each do |conference| |
|||
conference.registration_status = conference.registration_open ? :open : :closed |
|||
conference.save! |
|||
end |
|||
end |
|||
end |
@ -0,0 +1,5 @@ |
|||
class AddHighestStepToConferenceRegistrations < ActiveRecord::Migration |
|||
def change |
|||
add_column :conference_registrations, :highest_step, :string |
|||
end |
|||
end |
@ -0,0 +1,5 @@ |
|||
class AddStepsCompletedToConferenceRegistrations < ActiveRecord::Migration |
|||
def change |
|||
add_column :conference_registrations, :steps_completed, :json |
|||
end |
|||
end |
@ -0,0 +1,5 @@ |
|||
class AddNeedsFacilitatorsToWorkshops < ActiveRecord::Migration |
|||
def change |
|||
add_column :workshops, :needs_facilitators, :boolean |
|||
end |
|||
end |
@ -0,0 +1,5 @@ |
|||
class AddLanguagesToUsers < ActiveRecord::Migration |
|||
def change |
|||
add_column :users, :languages, :json |
|||
end |
|||
end |
@ -0,0 +1,5 @@ |
|||
class ChangeDateFormatInDynamicTranslationRecords < ActiveRecord::Migration |
|||
def change |
|||
change_column :dynamic_translation_records, :created_at, :timestamp |
|||
end |
|||
end |
@ -0,0 +1,78 @@ |
|||
/*! Licensed under MIT, https://github.com/sofish/pen */ |
|||
(function(root) { |
|||
|
|||
// only works with Pen
|
|||
if(!root.Pen) return; |
|||
|
|||
// markdown covertor obj
|
|||
var covertor = { |
|||
keymap: { '96': '`', '62': '>', '49': '1', '46': '.', '45': '-', '42': '*', '35': '#'}, |
|||
stack : [] |
|||
}; |
|||
|
|||
// return valid markdown syntax
|
|||
covertor.valid = function(str) { |
|||
var len = str.length; |
|||
|
|||
if(str.match(/[#]{1,6}/)) { |
|||
return ['h' + len, len]; |
|||
} else if(str === '```') { |
|||
return ['pre', len]; |
|||
} else if(str === '>') { |
|||
return ['blockquote', len]; |
|||
} else if(str === '1.') { |
|||
return ['insertorderedlist', len]; |
|||
} else if(str === '-' || str === '*') { |
|||
return ['insertunorderedlist', len]; |
|||
} else if(str.match(/(?:\.|\*|\-){3,}/)) { |
|||
return ['inserthorizontalrule', len]; |
|||
} |
|||
}; |
|||
|
|||
// parse command
|
|||
covertor.parse = function(e) { |
|||
var code = e.keyCode || e.which; |
|||
|
|||
// when `space` is pressed
|
|||
if (code === 32) { |
|||
var markdownSyntax = this.stack.join(''); |
|||
// reset stack
|
|||
this.stack = []; |
|||
|
|||
var cmd = this.valid(markdownSyntax); |
|||
if (cmd) { |
|||
// prevents leading space after executing command
|
|||
e.preventDefault(); |
|||
return cmd; |
|||
} |
|||
} |
|||
|
|||
// make cmd
|
|||
if(this.keymap[code]) this.stack.push(this.keymap[code]); |
|||
|
|||
return false; |
|||
}; |
|||
|
|||
// exec command
|
|||
covertor.action = function(pen, cmd) { |
|||
|
|||
// only apply effect at line start
|
|||
if(pen.selection.focusOffset > cmd[1]) return; |
|||
|
|||
var node = pen.selection.focusNode; |
|||
node.textContent = node.textContent.slice(cmd[1]); |
|||
pen.execCommand(cmd[0]); |
|||
}; |
|||
|
|||
// init covertor
|
|||
covertor.init = function(pen) { |
|||
pen.on('keypress', function(e) { |
|||
var cmd = covertor.parse(e); |
|||
if(cmd) return covertor.action(pen, cmd); |
|||
}); |
|||
}; |
|||
|
|||
// append to Pen
|
|||
root.Pen.prototype.markdown = covertor; |
|||
|
|||
}(window)); |
@ -0,0 +1,835 @@ |
|||
/*! Licensed under MIT, https://github.com/sofish/pen */ |
|||
(function(root, doc) { |
|||
|
|||
var Pen, debugMode, selection, utils = {}; |
|||
var toString = Object.prototype.toString; |
|||
var slice = Array.prototype.slice; |
|||
|
|||
// allow command list
|
|||
var commandsReg = { |
|||
block: /^(?:p|h[1-6]|blockquote|pre)$/, |
|||
inline: /^(?:bold|italic|underline|insertorderedlist|insertunorderedlist|indent|outdent|strikethrough)$/, |
|||
source: /^(?:createlink|unlink)$/, |
|||
insert: /^(?:inserthorizontalrule|insertimage|insert)$/, |
|||
wrap: /^(?:code)$/ |
|||
}; |
|||
|
|||
var lineBreakReg = /^(?:blockquote|pre|div)$/i; |
|||
|
|||
var effectNodeReg = /(?:[pubia]|h[1-6]|blockquote|[uo]l|li)/i; |
|||
|
|||
var strReg = { |
|||
whiteSpace: /(^\s+)|(\s+$)/g, |
|||
mailTo: /^(?!mailto:|.+\/|.+#|.+\?)(.*@.*\..+)$/, |
|||
http: /^(?!\w+?:\/\/|mailto:|\/|\.\/|\?|#)(.*)$/ |
|||
}; |
|||
|
|||
var autoLinkReg = { |
|||
url: /((https?|ftp):\/\/|www\.)[^\s<]{3,}/gi, |
|||
prefix: /^(?:https?|ftp):\/\//i, |
|||
notLink: /^(?:img|a|input|audio|video|source|code|pre|script|head|title|style)$/i, |
|||
maxLength: 100 |
|||
}; |
|||
|
|||
// type detect
|
|||
utils.is = function(obj, type) { |
|||
return toString.call(obj).slice(8, -1) === type; |
|||
}; |
|||
|
|||
utils.forEach = function(obj, iterator, arrayLike) { |
|||
if (!obj) return; |
|||
if (arrayLike == null) arrayLike = utils.is(obj, 'Array'); |
|||
if (arrayLike) { |
|||
for (var i = 0, l = obj.length; i < l; i++) iterator(obj[i], i, obj); |
|||
} else { |
|||
for (var key in obj) { |
|||
if (obj.hasOwnProperty(key)) iterator(obj[key], key, obj); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// copy props from a obj
|
|||
utils.copy = function(defaults, source) { |
|||
utils.forEach(source, function (value, key) { |
|||
defaults[key] = utils.is(value, 'Object') ? utils.copy({}, value) : |
|||
utils.is(value, 'Array') ? utils.copy([], value) : value; |
|||
}); |
|||
return defaults; |
|||
}; |
|||
|
|||
// log
|
|||
utils.log = function(message, force) { |
|||
if (debugMode || force) |
|||
console.log('%cPEN DEBUGGER: %c' + message, 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;'); |
|||
}; |
|||
|
|||
utils.delayExec = function (fn) { |
|||
var timer = null; |
|||
return function (delay) { |
|||
clearTimeout(timer); |
|||
timer = setTimeout(function() { |
|||
fn(); |
|||
}, delay || 1); |
|||
}; |
|||
}; |
|||
|
|||
// merge: make it easy to have a fallback
|
|||
utils.merge = function(config) { |
|||
|
|||
// default settings
|
|||
var defaults = { |
|||
class: 'pen', |
|||
debug: false, |
|||
toolbar: null, // custom toolbar
|
|||
stay: config.stay || !config.debug, |
|||
stayMsg: 'Are you going to leave here?', |
|||
textarea: '<textarea name="content"></textarea>', |
|||
list: [ |
|||
'blockquote', 'h2', 'h3', 'p', 'code', 'insertorderedlist', 'insertunorderedlist', 'inserthorizontalrule', |
|||
'indent', 'outdent', 'bold', 'italic', 'underline', 'createlink', 'insertimage' |
|||
], |
|||
titles: {}, |
|||
cleanAttrs: ['id', 'class', 'style', 'name'], |
|||
cleanTags: ['script'] |
|||
}; |
|||
|
|||
// user-friendly config
|
|||
if (config.nodeType === 1) { |
|||
defaults.editor = config; |
|||
} else if (config.match && config.match(/^#[\S]+$/)) { |
|||
defaults.editor = doc.getElementById(config.slice(1)); |
|||
} else { |
|||
defaults = utils.copy(defaults, config); |
|||
} |
|||
|
|||
return defaults; |
|||
}; |
|||
|
|||
function commandOverall(ctx, cmd, val) { |
|||
var message = ' to exec 「' + cmd + '」 command' + (val ? (' with value: ' + val) : ''); |
|||
|
|||
try { |
|||
doc.execCommand(cmd, false, val); |
|||
} catch(err) { |
|||
// TODO: there's an error when insert a image to document, bug not a bug
|
|||
return utils.log('fail' + message, true); |
|||
} |
|||
|
|||
utils.log('success' + message); |
|||
} |
|||
|
|||
function commandInsert(ctx, name, val) { |
|||
var node = getNode(ctx); |
|||
if (!node) return; |
|||
ctx._range.selectNode(node); |
|||
ctx._range.collapse(false); |
|||
|
|||
// hide menu when a image was inserted
|
|||
if(name === 'insertimage' && ctx._menu) toggleNode(ctx._menu, true); |
|||
|
|||
return commandOverall(ctx, name, val); |
|||
} |
|||
|
|||
function commandBlock(ctx, name) { |
|||
var list = effectNode(ctx, getNode(ctx), true); |
|||
if (list.indexOf(name) !== -1) name = 'p'; |
|||
return commandOverall(ctx, 'formatblock', name); |
|||
} |
|||
|
|||
function commandWrap(ctx, tag, value) { |
|||
value = '<' + tag + '>' + (value||selection.toString()) + '</' + tag + '>'; |
|||
return commandOverall(ctx, 'insertHTML', value); |
|||
} |
|||
|
|||
function initToolbar(ctx) { |
|||
var icons = '', inputStr = '<input class="pen-input" placeholder="http://" />'; |
|||
|
|||
ctx._toolbar = ctx.config.toolbar; |
|||
if (!ctx._toolbar) { |
|||
var toolList = ctx.config.list; |
|||
utils.forEach(toolList, function (name) { |
|||
var klass = 'pen-icon icon-' + name; |
|||
var title = ctx.config.titles[name] || ''; |
|||
icons += '<i class="' + klass + '" data-action="' + name + '" title="' + title + '"></i>'; |
|||
}, true); |
|||
if (toolList.indexOf('createlink') >= 0 || toolList.indexOf('insertimage') >= 0) |
|||
icons += inputStr; |
|||
} else if (ctx._toolbar.querySelectorAll('[data-action=createlink]').length || |
|||
ctx._toolbar.querySelectorAll('[data-action=insertimage]').length) { |
|||
icons += inputStr; |
|||
} |
|||
|
|||
if (icons) { |
|||
ctx._menu = doc.createElement('div'); |
|||
ctx._menu.setAttribute('class', ctx.config.class + '-menu pen-menu'); |
|||
ctx._menu.innerHTML = icons; |
|||
ctx._inputBar = ctx._menu.querySelector('input'); |
|||
toggleNode(ctx._menu, true); |
|||
doc.body.appendChild(ctx._menu); |
|||
} |
|||
if (ctx._toolbar && ctx._inputBar) toggleNode(ctx._inputBar); |
|||
} |
|||
|
|||
function initEvents(ctx) { |
|||
var toolbar = ctx._toolbar || ctx._menu, editor = ctx.config.editor; |
|||
|
|||
var toggleMenu = utils.delayExec(function() { |
|||
ctx.highlight().menu(); |
|||
}); |
|||
var outsideClick = function() {}; |
|||
|
|||
function updateStatus(delay) { |
|||
ctx._range = ctx.getRange(); |
|||
toggleMenu(delay); |
|||
} |
|||
|
|||
if (ctx._menu) { |
|||
var setpos = function() { |
|||
if (ctx._menu.style.display === 'block') ctx.menu(); |
|||
}; |
|||
|
|||
// change menu offset when window resize / scroll
|
|||
addListener(ctx, root, 'resize', setpos); |
|||
addListener(ctx, root, 'scroll', setpos); |
|||
|
|||
// toggle toolbar on mouse select
|
|||
var selecting = false; |
|||
addListener(ctx, editor, 'mousedown', function() { |
|||
selecting = true; |
|||
}); |
|||
addListener(ctx, editor, 'mouseleave', function() { |
|||
if (selecting) updateStatus(800); |
|||
selecting = false; |
|||
}); |
|||
addListener(ctx, editor, 'mouseup', function() { |
|||
if (selecting) updateStatus(100); |
|||
selecting = false; |
|||
}); |
|||
// Hide menu when focusing outside of editor
|
|||
outsideClick = function(e) { |
|||
if (ctx._menu && !containsNode(editor, e.target) && !containsNode(ctx._menu, e.target)) { |
|||
removeListener(ctx, doc, 'click', outsideClick); |
|||
toggleMenu(100); |
|||
} |
|||
}; |
|||
} else { |
|||
addListener(ctx, editor, 'click', function() { |
|||
updateStatus(0); |
|||
}); |
|||
} |
|||
|
|||
addListener(ctx, editor, 'keyup', function(e) { |
|||
if (e.which === 8 && ctx.isEmpty()) return lineBreak(ctx, true); |
|||
// toggle toolbar on key select
|
|||
if (e.which !== 13 || e.shiftKey) return updateStatus(400); |
|||
var node = getNode(ctx, true); |
|||
if (!node || !node.nextSibling || !lineBreakReg.test(node.nodeName)) return; |
|||
if (node.nodeName !== node.nextSibling.nodeName) return; |
|||
// hack for webkit, make 'enter' behavior like as firefox.
|
|||
if (node.lastChild.nodeName !== 'BR') node.appendChild(doc.createElement('br')); |
|||
utils.forEach(node.nextSibling.childNodes, function(child) { |
|||
if (child) node.appendChild(child); |
|||
}, true); |
|||
node.parentNode.removeChild(node.nextSibling); |
|||
focusNode(ctx, node.lastChild, ctx.getRange()); |
|||
}); |
|||
|
|||
// check line break
|
|||
addListener(ctx, editor, 'keydown', function(e) { |
|||
editor.classList.remove('pen-placeholder'); |
|||
if (e.which !== 13 || e.shiftKey) return; |
|||
var node = getNode(ctx, true); |
|||
if (!node || !lineBreakReg.test(node.nodeName)) return; |
|||
var lastChild = node.lastChild; |
|||
if (!lastChild || !lastChild.previousSibling) return; |
|||
if (lastChild.previousSibling.textContent || lastChild.textContent) return; |
|||
// quit block mode for 2 'enter'
|
|||
e.preventDefault(); |
|||
var p = doc.createElement('p'); |
|||
p.innerHTML = '<br>'; |
|||
node.removeChild(lastChild); |
|||
if (!node.nextSibling) node.parentNode.appendChild(p); |
|||
else node.parentNode.insertBefore(p, node.nextSibling); |
|||
focusNode(ctx, p, ctx.getRange()); |
|||
}); |
|||
|
|||
var menuApply = function(action, value) { |
|||
ctx.execCommand(action, value); |
|||
ctx._range = ctx.getRange(); |
|||
ctx.highlight().menu(); |
|||
}; |
|||
|
|||
// toggle toolbar on key select
|
|||
addListener(ctx, toolbar, 'click', function(e) { |
|||
var node = e.target, action; |
|||
|
|||
while (node !== toolbar && !(action = node.getAttribute('data-action'))) { |
|||
node = node.parentNode; |
|||
} |
|||
|
|||
if (!action) return; |
|||
if (!/(?:createlink)|(?:insertimage)/.test(action)) return menuApply(action); |
|||
if (!ctx._inputBar) return; |
|||
|
|||
// create link
|
|||
var input = ctx._inputBar; |
|||
if (toolbar === ctx._menu) toggleNode(input); |
|||
else { |
|||
ctx._inputActive = true; |
|||
ctx.menu(); |
|||
} |
|||
if (ctx._menu.style.display === 'none') return; |
|||
|
|||
setTimeout(function() { input.focus(); }, 400); |
|||
var createlink = function() { |
|||
var inputValue = input.value; |
|||
|
|||
if (!inputValue) action = 'unlink'; |
|||
else { |
|||
inputValue = input.value |
|||
.replace(strReg.whiteSpace, '') |
|||
.replace(strReg.mailTo, 'mailto:$1') |
|||
.replace(strReg.http, 'http://$1'); |
|||
} |
|||
menuApply(action, inputValue); |
|||
if (toolbar === ctx._menu) toggleNode(input, false); |
|||
else toggleNode(ctx._menu, true); |
|||
}; |
|||
|
|||
input.onkeypress = function(e) { |
|||
if (e.which === 13) return createlink(); |
|||
}; |
|||
|
|||
}); |
|||
|
|||
// listen for placeholder
|
|||
addListener(ctx, editor, 'focus', function() { |
|||
if (ctx.isEmpty()) lineBreak(ctx, true); |
|||
addListener(ctx, doc, 'click', outsideClick); |
|||
}); |
|||
|
|||
addListener(ctx, editor, 'blur', function() { |
|||
checkPlaceholder(ctx); |
|||
ctx.checkContentChange(); |
|||
}); |
|||
|
|||
// listen for paste and clear style
|
|||
addListener(ctx, editor, 'paste', function() { |
|||
setTimeout(function() { |
|||
ctx.cleanContent(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function addListener(ctx, target, type, listener) { |
|||
if (ctx._events.hasOwnProperty(type)) { |
|||
ctx._events[type].push(listener); |
|||
} else { |
|||
ctx._eventTargets = ctx._eventTargets || []; |
|||
ctx._eventsCache = ctx._eventsCache || []; |
|||
var index = ctx._eventTargets.indexOf(target); |
|||
if (index < 0) index = ctx._eventTargets.push(target) - 1; |
|||
ctx._eventsCache[index] = ctx._eventsCache[index] || {}; |
|||
ctx._eventsCache[index][type] = ctx._eventsCache[index][type] || []; |
|||
ctx._eventsCache[index][type].push(listener); |
|||
|
|||
target.addEventListener(type, listener, false); |
|||
} |
|||
return ctx; |
|||
} |
|||
|
|||
// trigger local events
|
|||
function triggerListener(ctx, type) { |
|||
if (!ctx._events.hasOwnProperty(type)) return; |
|||
var args = slice.call(arguments, 2); |
|||
utils.forEach(ctx._events[type], function (listener) { |
|||
listener.apply(ctx, args); |
|||
}); |
|||
} |
|||
|
|||
function removeListener(ctx, target, type, listener) { |
|||
var events = ctx._events[type]; |
|||
if (!events) { |
|||
var _index = ctx._eventTargets.indexOf(target); |
|||
if (_index >= 0) events = ctx._eventsCache[_index][type]; |
|||
} |
|||
if (!events) return ctx; |
|||
var index = events.indexOf(listener); |
|||
if (index >= 0) events.splice(index, 1); |
|||
target.removeEventListener(type, listener, false); |
|||
return ctx; |
|||
} |
|||
|
|||
function removeAllListeners(ctx) { |
|||
utils.forEach(this._events, function (events) { |
|||
events.length = 0; |
|||
}, false); |
|||
if (!ctx._eventsCache) return ctx; |
|||
utils.forEach(ctx._eventsCache, function (events, index) { |
|||
var target = ctx._eventTargets[index]; |
|||
utils.forEach(events, function (listeners, type) { |
|||
utils.forEach(listeners, function (listener) { |
|||
target.removeEventListener(type, listener, false); |
|||
}, true); |
|||
}, false); |
|||
}, true); |
|||
ctx._eventTargets = []; |
|||
ctx._eventsCache = []; |
|||
return ctx; |
|||
} |
|||
|
|||
function checkPlaceholder(ctx) { |
|||
ctx.config.editor.classList[ctx.isEmpty() ? 'add' : 'remove']('pen-placeholder'); |
|||
} |
|||
|
|||
function trim(str) { |
|||
return (str || '').replace(/^\s+|\s+$/g, ''); |
|||
} |
|||
|
|||
// node.contains is not implemented in IE10/IE11
|
|||
function containsNode(parent, child) { |
|||
if (parent === child) return true; |
|||
child = child.parentNode; |
|||
while (child) { |
|||
if (child === parent) return true; |
|||
child = child.parentNode; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
function getNode(ctx, byRoot) { |
|||
var node, root = ctx.config.editor; |
|||
ctx._range = ctx._range || ctx.getRange(); |
|||
node = ctx._range.commonAncestorContainer; |
|||
if (!node || node === root) return null; |
|||
while (node && (node.nodeType !== 1) && (node.parentNode !== root)) node = node.parentNode; |
|||
while (node && byRoot && (node.parentNode !== root)) node = node.parentNode; |
|||
return containsNode(root, node) ? node : null; |
|||
} |
|||
|
|||
// node effects
|
|||
function effectNode(ctx, el, returnAsNodeName) { |
|||
var nodes = []; |
|||
el = el || ctx.config.editor; |
|||
while (el && el !== ctx.config.editor) { |
|||
if (el.nodeName.match(effectNodeReg)) { |
|||
nodes.push(returnAsNodeName ? el.nodeName.toLowerCase() : el); |
|||
} |
|||
el = el.parentNode; |
|||
} |
|||
return nodes; |
|||
} |
|||
|
|||
// breakout from node
|
|||
function lineBreak(ctx, empty) { |
|||
var range = ctx._range = ctx.getRange(), node = doc.createElement('p'); |
|||
if (empty) ctx.config.editor.innerHTML = ''; |
|||
node.innerHTML = '<br>'; |
|||
range.insertNode(node); |
|||
focusNode(ctx, node.childNodes[0], range); |
|||
} |
|||
|
|||
function focusNode(ctx, node, range) { |
|||
range.setStartAfter(node); |
|||
range.setEndBefore(node); |
|||
range.collapse(false); |
|||
ctx.setRange(range); |
|||
} |
|||
|
|||
function autoLink(node) { |
|||
if (node.nodeType === 1) { |
|||
if (autoLinkReg.notLink.test(node.tagName)) return; |
|||
utils.forEach(node.childNodes, function (child) { |
|||
autoLink(child); |
|||
}, true); |
|||
} else if (node.nodeType === 3) { |
|||
var result = urlToLink(node.nodeValue || ''); |
|||
if (!result.links) return; |
|||
var frag = doc.createDocumentFragment(), |
|||
div = doc.createElement('div'); |
|||
div.innerHTML = result.text; |
|||
while (div.childNodes.length) frag.appendChild(div.childNodes[0]); |
|||
node.parentNode.replaceChild(frag, node); |
|||
} |
|||
} |
|||
|
|||
function urlToLink(str) { |
|||
var count = 0; |
|||
str = str.replace(autoLinkReg.url, function(url) { |
|||
var realUrl = url, displayUrl = url; |
|||
count++; |
|||
if (url.length > autoLinkReg.maxLength) displayUrl = url.slice(0, autoLinkReg.maxLength) + '...'; |
|||
// Add http prefix if necessary
|
|||
if (!autoLinkReg.prefix.test(realUrl)) realUrl = 'http://' + realUrl; |
|||
return '<a href="' + realUrl + '">' + displayUrl + '</a>'; |
|||
}); |
|||
return {links: count, text: str}; |
|||
} |
|||
|
|||
function toggleNode(node, hide) { |
|||
node.style.display = hide ? 'none' : 'block'; |
|||
} |
|||
|
|||
Pen = function(config) { |
|||
|
|||
if (!config) throw new Error('Can\'t find config'); |
|||
|
|||
debugMode = config.debug; |
|||
|
|||
// merge user config
|
|||
var defaults = utils.merge(config); |
|||
|
|||
var editor = defaults.editor; |
|||
|
|||
if (!editor || editor.nodeType !== 1) throw new Error('Can\'t find editor'); |
|||
|
|||
// set default class
|
|||
editor.classList.add(defaults.class); |
|||
|
|||
// set contenteditable
|
|||
editor.setAttribute('contenteditable', 'true'); |
|||
|
|||
// assign config
|
|||
this.config = defaults; |
|||
|
|||
// set placeholder
|
|||
if (defaults.placeholder) editor.setAttribute('data-placeholder', defaults.placeholder); |
|||
checkPlaceholder(this); |
|||
|
|||
// save the selection obj
|
|||
this.selection = selection; |
|||
|
|||
// define local events
|
|||
this._events = {change: []}; |
|||
|
|||
// enable toolbar
|
|||
initToolbar(this); |
|||
|
|||
// init events
|
|||
initEvents(this); |
|||
|
|||
// to check content change
|
|||
this._prevContent = this.getContent(); |
|||
|
|||
// enable markdown covert
|
|||
if (this.markdown) this.markdown.init(this); |
|||
|
|||
// stay on the page
|
|||
if (this.config.stay) this.stay(this.config); |
|||
|
|||
}; |
|||
|
|||
Pen.prototype.on = function(type, listener) { |
|||
addListener(this, this.config.editor, type, listener); |
|||
return this; |
|||
}; |
|||
|
|||
Pen.prototype.isEmpty = function(node) { |
|||
node = node || this.config.editor; |
|||
return !(node.querySelector('img')) && !(node.querySelector('blockquote')) && |
|||
!(node.querySelector('li')) && !trim(node.textContent); |
|||
}; |
|||
|
|||
Pen.prototype.getContent = function() { |
|||
return this.isEmpty() ? '' : trim(this.config.editor.innerHTML); |
|||
}; |
|||
|
|||
Pen.prototype.setContent = function(html) { |
|||
this.config.editor.innerHTML = html; |
|||
this.cleanContent(); |
|||
return this; |
|||
}; |
|||
|
|||
Pen.prototype.checkContentChange = function () { |
|||
var prevContent = this._prevContent, currentContent = this.getContent(); |
|||
if (prevContent === currentContent) return; |
|||
this._prevContent = currentContent; |
|||
triggerListener(this, 'change', currentContent, prevContent); |
|||
}; |
|||
|
|||
Pen.prototype.getRange = function() { |
|||
var editor = this.config.editor, range = selection.rangeCount && selection.getRangeAt(0); |
|||
if (!range) range = doc.createRange(); |
|||
if (!containsNode(editor, range.commonAncestorContainer)) { |
|||
range.selectNodeContents(editor); |
|||
range.collapse(false); |
|||
} |
|||
return range; |
|||
}; |
|||
|
|||
Pen.prototype.setRange = function(range) { |
|||
range = range || this._range; |
|||
if (!range) { |
|||
range = this.getRange(); |
|||
range.collapse(false); // set to end
|
|||
} |
|||
try { |
|||
selection.removeAllRanges(); |
|||
selection.addRange(range); |
|||
} catch (e) {/* IE throws error sometimes*/} |
|||
return this; |
|||
}; |
|||
|
|||
Pen.prototype.focus = function(focusStart) { |
|||
if (!focusStart) this.setRange(); |
|||
this.config.editor.focus(); |
|||
return this; |
|||
}; |
|||
|
|||
Pen.prototype.execCommand = function(name, value) { |
|||
name = name.toLowerCase(); |
|||
this.setRange(); |
|||
|
|||
if (commandsReg.block.test(name)) { |
|||
commandBlock(this, name); |
|||
} else if (commandsReg.inline.test(name) || commandsReg.source.test(name)) { |
|||
commandOverall(this, name, value); |
|||
} else if (commandsReg.insert.test(name)) { |
|||
commandInsert(this, name, value); |
|||
} else if (commandsReg.wrap.test(name)) { |
|||
commandWrap(this, name, value); |
|||
} else { |
|||
utils.log('can not find command function for name: ' + name + (value ? (', value: ' + value) : ''), true); |
|||
} |
|||
if (name === 'indent') this.checkContentChange(); |
|||
else this.cleanContent({cleanAttrs: ['style']}); |
|||
}; |
|||
|
|||
// remove attrs and tags
|
|||
// pen.cleanContent({cleanAttrs: ['style'], cleanTags: ['id']})
|
|||
Pen.prototype.cleanContent = function(options) { |
|||
var editor = this.config.editor; |
|||
|
|||
if (!options) options = this.config; |
|||
utils.forEach(options.cleanAttrs, function (attr) { |
|||
utils.forEach(editor.querySelectorAll('[' + attr + ']'), function(item) { |
|||
item.removeAttribute(attr); |
|||
}, true); |
|||
}, true); |
|||
utils.forEach(options.cleanTags, function (tag) { |
|||
utils.forEach(editor.querySelectorAll(tag), function(item) { |
|||
item.parentNode.removeChild(item); |
|||
}, true); |
|||
}, true); |
|||
|
|||
checkPlaceholder(this); |
|||
this.checkContentChange(); |
|||
return this; |
|||
}; |
|||
|
|||
// auto link content, return content
|
|||
Pen.prototype.autoLink = function() { |
|||
autoLink(this.config.editor); |
|||
return this.getContent(); |
|||
}; |
|||
|
|||
// highlight menu
|
|||
Pen.prototype.highlight = function() { |
|||
var toolbar = this._toolbar || this._menu |
|||
, node = getNode(this); |
|||
// remove all highlights
|
|||
utils.forEach(toolbar.querySelectorAll('.active'), function(el) { |
|||
el.classList.remove('active'); |
|||
}, true); |
|||
|
|||
if (!node) return this; |
|||
|
|||
var effects = effectNode(this, node) |
|||
, inputBar = this._inputBar |
|||
, highlight; |
|||
|
|||
if (inputBar && toolbar === this._menu) { |
|||
// display link input if createlink enabled
|
|||
inputBar.style.display = 'none'; |
|||
// reset link input value
|
|||
inputBar.value = ''; |
|||
} |
|||
|
|||
highlight = function(str) { |
|||
if (!str) return; |
|||
var el = toolbar.querySelector('[data-action=' + str + ']'); |
|||
return el && el.classList.add('active'); |
|||
}; |
|||
utils.forEach(effects, function(item) { |
|||
var tag = item.nodeName.toLowerCase(); |
|||
switch(tag) { |
|||
case 'a': |
|||
if (inputBar) inputBar.value = item.getAttribute('href'); |
|||
tag = 'createlink'; |
|||
break; |
|||
case 'img': |
|||
if (inputBar) inputBar.value = item.getAttribute('src'); |
|||
tag = 'insertimage'; |
|||
break; |
|||
case 'i': |
|||
tag = 'italic'; |
|||
break; |
|||
case 'u': |
|||
tag = 'underline'; |
|||
break; |
|||
case 'strike': |
|||
tag = 'strikethrough'; |
|||
break; |
|||
case 'b': |
|||
tag = 'bold'; |
|||
break; |
|||
case 'pre': |
|||
case 'code': |
|||
tag = 'code'; |
|||
break; |
|||
case 'ul': |
|||
tag = 'insertunorderedlist'; |
|||
break; |
|||
case 'ol': |
|||
tag = 'insertorderedlist'; |
|||
break; |
|||
case 'li': |
|||
tag = 'indent'; |
|||
break; |
|||
} |
|||
highlight(tag); |
|||
}, true); |
|||
|
|||
return this; |
|||
}; |
|||
|
|||
// show menu
|
|||
Pen.prototype.menu = function() { |
|||
if (!this._menu) return this; |
|||
if (selection.isCollapsed) { |
|||
this._menu.style.display = 'none'; //hide menu
|
|||
this._inputActive = false; |
|||
return this; |
|||
} |
|||
if (this._toolbar) { |
|||
if (!this._inputBar || !this._inputActive) return this; |
|||
} |
|||
var offset = this._range.getBoundingClientRect() |
|||
, menuPadding = 10 |
|||
, top = offset.top - menuPadding |
|||
, left = offset.left + (offset.width / 2) |
|||
, menu = this._menu |
|||
, menuOffset = {x: 0, y: 0} |
|||
, stylesheet = this._stylesheet; |
|||
|
|||
// fixes some browser double click visual discontinuity
|
|||
// if the offset has no width or height it should not be used
|
|||
if (offset.width === 0 && offset.height === 0) return this; |
|||
|
|||
// store the stylesheet used for positioning the menu horizontally
|
|||
if (this._stylesheet === undefined) { |
|||
var style = document.createElement("style"); |
|||
document.head.appendChild(style); |
|||
this._stylesheet = stylesheet = style.sheet; |
|||
} |
|||
// display block to caculate its width & height
|
|||
menu.style.display = 'block'; |
|||
|
|||
menuOffset.x = left - (menu.clientWidth / 2); |
|||
menuOffset.y = top - menu.clientHeight; |
|||
|
|||
// check to see if menu has over-extended its bounding box. if it has,
|
|||
// 1) apply a new class if overflowed on top;
|
|||
// 2) apply a new rule if overflowed on the left
|
|||
if (stylesheet.cssRules.length > 0) { |
|||
stylesheet.deleteRule(0); |
|||
} |
|||
if (menuOffset.x < 0) { |
|||
menuOffset.x = 0; |
|||
stylesheet.insertRule('.pen-menu:after {left: ' + left + 'px;}', 0); |
|||
} else { |
|||
stylesheet.insertRule('.pen-menu:after {left: 50%; }', 0); |
|||
} |
|||
if (menuOffset.y < 0) { |
|||
menu.classList.add('pen-menu-below'); |
|||
menuOffset.y = offset.top + offset.height + menuPadding; |
|||
} else { |
|||
menu.classList.remove('pen-menu-below'); |
|||
} |
|||
|
|||
menu.style.top = menuOffset.y + 'px'; |
|||
menu.style.left = menuOffset.x + 'px'; |
|||
return this; |
|||
}; |
|||
|
|||
Pen.prototype.stay = function(config) { |
|||
var ctx = this; |
|||
if (!window.onbeforeunload) { |
|||
window.onbeforeunload = function() { |
|||
if (!ctx._isDestroyed) return config.stayMsg; |
|||
}; |
|||
} |
|||
}; |
|||
|
|||
Pen.prototype.destroy = function(isAJoke) { |
|||
var destroy = isAJoke ? false : true |
|||
, attr = isAJoke ? 'setAttribute' : 'removeAttribute'; |
|||
|
|||
if (!isAJoke) { |
|||
removeAllListeners(this); |
|||
try { |
|||
selection.removeAllRanges(); |
|||
if (this._menu) this._menu.parentNode.removeChild(this._menu); |
|||
} catch (e) {/* IE throws error sometimes*/} |
|||
} else { |
|||
initToolbar(this); |
|||
initEvents(this); |
|||
} |
|||
this._isDestroyed = destroy; |
|||
this.config.editor[attr]('contenteditable', ''); |
|||
|
|||
return this; |
|||
}; |
|||
|
|||
Pen.prototype.rebuild = function() { |
|||
return this.destroy('it\'s a joke'); |
|||
}; |
|||
|
|||
// a fallback for old browers
|
|||
root.Pen = function(config) { |
|||
if (!config) return utils.log('can\'t find config', true); |
|||
|
|||
var defaults = utils.merge(config) |
|||
, klass = defaults.editor.getAttribute('class'); |
|||
|
|||
klass = klass ? klass.replace(/\bpen\b/g, '') + ' pen-textarea ' + defaults.class : 'pen pen-textarea'; |
|||
defaults.editor.setAttribute('class', klass); |
|||
defaults.editor.innerHTML = defaults.textarea; |
|||
return defaults.editor; |
|||
}; |
|||
|
|||
// export content as markdown
|
|||
var regs = { |
|||
a: [/<a\b[^>]*href=["']([^"]+|[^']+)\b[^>]*>(.*?)<\/a>/ig, '[$2]($1)'], |
|||
img: [/<img\b[^>]*src=["']([^\"+|[^']+)[^>]*>/ig, '![]($1)'], |
|||
b: [/<b\b[^>]*>(.*?)<\/b>/ig, '**$1**'], |
|||
i: [/<i\b[^>]*>(.*?)<\/i>/ig, '***$1***'], |
|||
h: [/<h([1-6])\b[^>]*>(.*?)<\/h\1>/ig, function(a, b, c) { |
|||
return '\n' + ('######'.slice(0, b)) + ' ' + c + '\n'; |
|||
}], |
|||
li: [/<(li)\b[^>]*>(.*?)<\/\1>/ig, '* $2\n'], |
|||
blockquote: [/<(blockquote)\b[^>]*>(.*?)<\/\1>/ig, '\n> $2\n'], |
|||
pre: [/<pre\b[^>]*>(.*?)<\/pre>/ig, '\n```\n$1\n```\n'], |
|||
p: [/<p\b[^>]*>(.*?)<\/p>/ig, '\n$1\n'], |
|||
hr: [/<hr\b[^>]*>/ig, '\n---\n'] |
|||
}; |
|||
|
|||
Pen.prototype.toMd = function() { |
|||
var html = this.getContent() |
|||
.replace(/\n+/g, '') // remove line break
|
|||
.replace(/<([uo])l\b[^>]*>(.*?)<\/\1l>/ig, '$2'); // remove ul/ol
|
|||
|
|||
for(var p in regs) { |
|||
if (regs.hasOwnProperty(p)) |
|||
html = html.replace.apply(html, regs[p]); |
|||
} |
|||
return html.replace(/\*{5}/g, '**'); |
|||
}; |
|||
|
|||
// make it accessible
|
|||
if (doc.getSelection) { |
|||
selection = doc.getSelection(); |
|||
root.Pen = Pen; |
|||
} |
|||
|
|||
}(window, document)); |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,159 @@ |
|||
/*! Licensed under MIT, https://github.com/sofish/pen */ |
|||
|
|||
/* basic reset */ |
|||
.pen:focus{outline:none;} |
|||
/*.pen, .pen-menu, .pen-input, .pen textarea{font:400 1.16em/1.45 Palatino, Optima, Georgia, serif;color:#331;} |
|||
.pen fieldset, img {border: 0;} |
|||
.pen blockquote{padding-left:10px;margin-left:-14px;border-left:4px solid #1abf89;} |
|||
.pen a{color:#1abf89;} |
|||
.pen del{text-decoration:line-through;} |
|||
.pen sub, .pen sup {font-size:75%;position:relative;vertical-align:text-top;} |
|||
:root .pen sub, :root .pen sup{vertical-align:baseline; /* for ie9 and other mordern browsers *//*} |
|||
.pen sup {top:-0.5em;} |
|||
.pen sub {bottom:-0.25em;} |
|||
.pen hr{border:none;border-bottom:1px solid #cfcfcf;margin-bottom:25px;*color:pink;*filter:chroma(color=pink);height:10px;*margin:-7px 0 15px;} |
|||
.pen small{font-size:0.8em;color:#888;} |
|||
.pen em, .pen b, .pen strong{font-weight:700;} |
|||
.pen pre{white-space:pre-wrap;padding:0.85em;background:#f8f8f8;} |
|||
*/ |
|||
/* block-level element margin */ |
|||
/*.pen p, .pen pre, .pen ul, .pen ol, .pen dl, .pen form, .pen table, .pen blockquote{margin-bottom:16px;} |
|||
*/ |
|||
/* headers */ |
|||
/*.pen h1, .pen h2, .pen h3, .pen h4, .pen h5, .pen h6{margin-bottom:16px;font-weight:700;line-height:1.2;} |
|||
.pen h1{font-size:2em;} |
|||
.pen h2{font-size:1.8em;} |
|||
.pen h3{font-size:1.6em;} |
|||
.pen h4{font-size:1.4em;} |
|||
.pen h5, .pen h6{font-size:1.2em;}*/ |
|||
|
|||
/* list */ |
|||
/*.pen ul, .pen ol{margin-left:1.2em;} |
|||
.pen ul, .pen-ul{list-style:disc;} |
|||
.pen ol, .pen-ol{list-style:decimal;} |
|||
.pen li ul, .pen li ol, .pen-ul ul, .pen-ul ol, .pen-ol ul, .pen-ol ol{margin:0 2em 0 1.2em;} |
|||
.pen li ul, .pen-ul ul, .pen-ol ul{list-style: circle;}*/ |
|||
|
|||
/* pen menu */ |
|||
.pen-menu [class^="icon-"], .pen-menu [class*=" icon-"] { /* reset to avoid conflicts with Bootstrap */ |
|||
background: transparent; |
|||
background-image: none; |
|||
} |
|||
.pen-menu { min-width: 320px; } |
|||
.pen-menu, .pen-input{font-size:14px;line-height:1;} |
|||
.pen-menu{white-space:nowrap;box-shadow:1px 2px 3px -2px #222;background:#333;background-image:linear-gradient(to bottom, #222, #333);opacity:0.9;position:fixed;height:36px;border:1px solid #333;border-radius:3px;display:none;z-index:1000;} |
|||
.pen-menu:after {top:100%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none;} |
|||
.pen-menu:after {border-color:rgba(51, 51, 51, 0);border-top-color:#333;border-width:6px;left:50%;margin-left:-6px;} |
|||
.pen-menu-below:after {top: -11px; display:block; -moz-transform: rotate(180deg); -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg);} |
|||
.pen-icon{ |
|||
font-size: 16px; |
|||
line-height: 40px;;min-width:20px;display:inline-block;padding:0 10px;height:36px;overflow:hidden;color:#fff;text-align:center;cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;} |
|||
.pen-icon:first-of-type{border-top-left-radius:3px;border-bottom-left-radius:3px;} |
|||
.pen-icon:last-of-type{border-top-right-radius:3px;border-bottom-right-radius:3px;} |
|||
.pen-icon:hover{background:#000;} |
|||
.pen-icon.active{color:#1abf89;background:#000;box-shadow:inset 2px 2px 4px #000;} |
|||
.pen-input{position:absolute;width:100%;left:0;top:0;height:36px;line-height:20px;background:#333;color:#fff;border:none;text-align:center;display:none;font-family:arial, sans-serif;} |
|||
.pen-input:focus{outline:none;} |
|||
|
|||
.pen-textarea{display:block;background:#f8f8f8;padding:20px;} |
|||
.pen textarea{font-size:14px;border:none;background:none;width:100%;_height:200px;min-height:200px;resize:none;} |
|||
|
|||
/*@font-face { |
|||
font-family: 'pen'; |
|||
src: url('font/fontello.eot?370dad08'); |
|||
src: url('font/fontello.eot?370dad08#iefix') format('embedded-opentype'), |
|||
url('font/fontello.woff?370dad08') format('woff'), |
|||
url('font/fontello.ttf?370dad08') format('truetype'), |
|||
url('font/fontello.svg?370dad08#fontello') format('svg'); |
|||
font-weight: normal; |
|||
font-style: normal; |
|||
}*/ |
|||
|
|||
.pen-menu [class^="icon-"]:before, .pen-menu [class*=" icon-"]:before { |
|||
speak: none; |
|||
display: inline-block; |
|||
width: 1em; |
|||
margin-right: .2em; |
|||
text-align: center; |
|||
font-variant: normal; |
|||
text-transform: none; |
|||
line-height: 1em; |
|||
margin-left: .2em; |
|||
} |
|||
|
|||
.pen-menu .icon-location:before { content: '\e815'; } /* '' */ |
|||
.pen-menu .icon-fit:before { content: '\e80f'; } /* '' */ |
|||
.pen-menu .icon-justifyleft:before { content: '\e80a'; } /* '' */ |
|||
.pen-menu .icon-justifycenter:before { content: '\e80b'; } /* '' */ |
|||
.pen-menu .icon-justifyright:before { content: '\e80c'; } /* '' */ |
|||
.pen-menu .icon-justifyfull:before { content: '\e80d'; } /* '' */ |
|||
.pen-menu .icon-outdent:before { content: '\e800'; } /* '' */ |
|||
.pen-menu .icon-indent:before { content: '\e801'; } /* '' */ |
|||
.pen-menu .icon-mode:before { content: '\e813'; } /* '' */ |
|||
.pen-menu .icon-fullscreen:before { content: '\e80e'; } /* '' */ |
|||
.pen-menu .icon-undo:before { content: '\e817'; } /* '' */ |
|||
.pen-menu .icon-code:before { content: '\e816'; } /* '' */ |
|||
.pen-menu .icon-pre:before { content: '\e816'; } /* '' */ |
|||
.pen-menu .icon-unlink:before { content: '\e811'; } /* '' */ |
|||
.pen-menu .icon-superscript:before { content: '\e808'; } /* '' */ |
|||
.pen-menu .icon-subscript:before { content: '\e809'; } /* '' */ |
|||
.pen-menu .icon-inserthorizontalrule:before { content: '\e818'; } /* '' */ |
|||
.pen-menu .icon-pin:before { content: '\e812'; } /* '' */ |
|||
.pen { |
|||
position: relative; |
|||
} |
|||
/*.pen.hinted h1:before, |
|||
.pen.hinted h2:before, |
|||
.pen.hinted h3:before, |
|||
.pen.hinted h4:before, |
|||
.pen.hinted h5:before, |
|||
.pen.hinted h6:before, |
|||
.pen.hinted blockquote:before, |
|||
.pen.hinted hr:before { |
|||
color: #eee; |
|||
position: absolute; |
|||
right: 100%; |
|||
white-space: nowrap; |
|||
padding-right: 10px; |
|||
} |
|||
.pen.hinted blockquote { border-left: 0; margin-left: 0; padding-left: 0; } |
|||
.pen.hinted blockquote:before { |
|||
color: #1abf89; |
|||
content: ">"; |
|||
font-weight: bold; |
|||
vertical-align: center; |
|||
} |
|||
.pen.hinted h1:before { content: "#";} |
|||
.pen.hinted h2:before { content: "##";} |
|||
.pen.hinted h3:before { content: "###";} |
|||
.pen.hinted h4:before { content: "####";} |
|||
.pen.hinted h5:before { content: "#####";} |
|||
.pen.hinted h6:before { content: "######";} |
|||
.pen.hinted hr:before { content: "﹘﹘﹘"; line-height: 1.2; vertical-align: bottom; } |
|||
|
|||
.pen.hinted pre:before, .pen.hinted pre:after { |
|||
content: "```"; |
|||
display: block; |
|||
color: #ccc; |
|||
} |
|||
|
|||
.pen.hinted ul { list-style: none; } |
|||
.pen.hinted ul li:before { |
|||
content: "*"; |
|||
color: #999; |
|||
line-height: 1; |
|||
vertical-align: bottom; |
|||
margin-left: -1.2em; |
|||
display: inline-block; |
|||
width: 1.2em; |
|||
} |
|||
|
|||
.pen.hinted b:before, .pen.hinted b:after { content: "**"; color: #eee; font-weight: normal; } |
|||
.pen.hinted i:before, .pen.hinted i:after { content: "*"; color: #eee; } |
|||
|
|||
.pen.hinted a { text-decoration: none; } |
|||
.pen.hinted a:before {content: "["; color: #ddd; } |
|||
.pen.hinted a:after { content: "](" attr(href) ")"; color: #ddd; } |
|||
|
|||
.pen-placeholder:after { position: absolute; top: 0; left: 0; content: attr(data-placeholder); color: #999; cursor: text; } |
|||
*/ |
Loading…
Reference in new issue