Added emojis. The emoji picker is coming soon, as is the font file. More emojis coming soon ^_^

Added the ability to ban and unban users.

Improved the installation instructions.
Changed the collation of the tables from utf8_general_ci to utf8mb4_general_ci.
Changed the group ID order.
Custom pages are now templates rather than little snippets being inserted into a template.
Changed the collation of the driver to utf8mb4_general_ci.
Added a default_group config setting.
Ban flag is overwritten for admins and super mods.
This commit is contained in:
Azareal 2016-12-08 14:11:18 +00:00
parent 6a320edbb4
commit 12abd657e1
20 changed files with 1833 additions and 78 deletions

View File

@ -24,14 +24,17 @@ Instructions on how to do so on Linux: https://downloads.mariadb.org/mariadb/rep
**Run the following commands:** **Run the following commands:**
go get github.com/go-sql-driver/mysql
go install github.com/go-sql-driver/mysql go install github.com/go-sql-driver/mysql
go get golang.org/x/crypto/bcrypt
go install golang.org/x/crypto/bcrypt go install golang.org/x/crypto/bcrypt
Tweak the config.go file and put your database details in there. Import data.sql into the same database. Comment out the first line (put /* and */ around it), if you've already made a database, and don't want the script to generate it for you. Tweak the config.go file and put your database details in there. Import data.sql into the same database. Comment out the first line (put /* and */ around it), if you've already made a database, and don't want the script to generate it for you.
Set the password column of your user account in the database to what you want your password to be. The system will encrypt your password when you login for the first time. Set the password column of your user account in the database to what you want your password to be. The system will encrypt your password when you login for the first time.
Add -u after go get to update those libraries, if you've already got them installed.
# Run the program # Run the program

View File

@ -12,7 +12,9 @@ var max_request_size = 5 * megabyte
// Misc // Misc
var default_route = route_topics var default_route = route_topics
var staff_css = "background-color: #ffeaff;" var default_group = 3
var staff_css = " background-color: #ffeaff;"
var uncategorised_forum_visible = true var uncategorised_forum_visible = true
var siteurl = "localhost:8080" var siteurl = "localhost:8080"
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + siteurl + ".png" var noavatar = "https://api.adorable.io/avatars/285/{id}@" + siteurl + ".png"
var items_per_page = 50

View File

@ -13,7 +13,7 @@ CREATE TABLE `users`(
`email` varchar(200) DEFAULT '' not null, `email` varchar(200) DEFAULT '' not null,
`avatar` varchar(20) DEFAULT '' not null, `avatar` varchar(20) DEFAULT '' not null,
primary key(`uid`) primary key(`uid`)
); ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `users_groups`( CREATE TABLE `users_groups`(
`gid` int not null AUTO_INCREMENT, `gid` int not null AUTO_INCREMENT,
@ -24,7 +24,7 @@ CREATE TABLE `users_groups`(
`is_banned` tinyint DEFAULT 0 not null, `is_banned` tinyint DEFAULT 0 not null,
`tag` varchar(50) DEFAULT '' not null, `tag` varchar(50) DEFAULT '' not null,
primary key(`gid`) primary key(`gid`)
); ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `forums`( CREATE TABLE `forums`(
`fid` int not null AUTO_INCREMENT, `fid` int not null AUTO_INCREMENT,
@ -36,7 +36,7 @@ CREATE TABLE `forums`(
`lastReplyerID` int DEFAULT 0 not null, `lastReplyerID` int DEFAULT 0 not null,
`lastTopicTime` datetime not null, `lastTopicTime` datetime not null,
primary key(`fid`) primary key(`fid`)
); ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `topics`( CREATE TABLE `topics`(
`tid` int not null AUTO_INCREMENT, `tid` int not null AUTO_INCREMENT,
@ -50,7 +50,7 @@ CREATE TABLE `topics`(
`sticky` tinyint DEFAULT 0 not null, `sticky` tinyint DEFAULT 0 not null,
`parentID` int DEFAULT 1 not null, `parentID` int DEFAULT 1 not null,
primary key(`tid`) primary key(`tid`)
); ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `replies`( CREATE TABLE `replies`(
`rid` int not null AUTO_INCREMENT, `rid` int not null AUTO_INCREMENT,
@ -62,7 +62,7 @@ CREATE TABLE `replies`(
`lastEdit` int not null, `lastEdit` int not null,
`lastEditBy` int not null, `lastEditBy` int not null,
primary key(`rid`) primary key(`rid`)
); ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `users_replies`( CREATE TABLE `users_replies`(
`rid` int not null AUTO_INCREMENT, `rid` int not null AUTO_INCREMENT,
@ -74,7 +74,7 @@ CREATE TABLE `users_replies`(
`lastEdit` int not null, `lastEdit` int not null,
`lastEditBy` int not null, `lastEditBy` int not null,
primary key(`rid`) primary key(`rid`)
); ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
/*CREATE TABLE `replies_reports` ( /*CREATE TABLE `replies_reports` (
`rid` int not null AUTO_INCREMENT, `rid` int not null AUTO_INCREMENT,
@ -86,9 +86,12 @@ CREATE TABLE `users_replies`(
INSERT INTO users(`name`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`) INSERT INTO users(`name`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`)
VALUES ('Admin',1,1,NOW(),NOW()); VALUES ('Admin',1,1,NOW(),NOW());
INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{}',1,1,"Admin"); INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{}',1,1,"Admin");
INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{}');
INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`tag`) VALUES ('Moderator','{}',1,"Mod"); INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`tag`) VALUES ('Moderator','{}',1,"Mod");
INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{}');
INSERT INTO users_groups(`name`,`permissions`,`is_banned`) VALUES ('Banned','{}',1);
INSERT INTO forums(`name`,`lastTopicTime`) VALUES ('General',NOW()); INSERT INTO forums(`name`,`lastTopicTime`) VALUES ('General',NOW());
INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`) INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`)
VALUES ('Test Topic','A topic automatically generated by the software.',NOW(),NOW(),1,1); VALUES ('Test Topic','A topic automatically generated by the software.',NOW(),NOW(),1,1);

Binary file not shown.

Binary file not shown.

58
main.go
View File

@ -6,7 +6,10 @@ import (
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"log" "log"
"mime" "mime"
"strings"
"strconv"
"path/filepath" "path/filepath"
"os"
"io/ioutil" "io/ioutil"
"html/template" "html/template"
) )
@ -40,6 +43,7 @@ var set_avatar_stmt *sql.Stmt
var set_username_stmt *sql.Stmt var set_username_stmt *sql.Stmt
var register_stmt *sql.Stmt var register_stmt *sql.Stmt
var username_exists_stmt *sql.Stmt var username_exists_stmt *sql.Stmt
var change_group_stmt *sql.Stmt
var create_profile_reply_stmt *sql.Stmt var create_profile_reply_stmt *sql.Stmt
var edit_profile_reply_stmt *sql.Stmt var edit_profile_reply_stmt *sql.Stmt
var delete_profile_reply_stmt *sql.Stmt var delete_profile_reply_stmt *sql.Stmt
@ -48,8 +52,8 @@ var create_forum_stmt *sql.Stmt
var delete_forum_stmt *sql.Stmt var delete_forum_stmt *sql.Stmt
var update_forum_stmt *sql.Stmt var update_forum_stmt *sql.Stmt
var custom_pages map[string]string = make(map[string]string)
var templates = template.Must(template.ParseGlob("templates/*")) var templates = template.Must(template.ParseGlob("templates/*"))
var custom_pages = template.Must(template.ParseGlob("pages/*"))
var no_css_tmpl = template.CSS("") var no_css_tmpl = template.CSS("")
var staff_css_tmpl = template.CSS(staff_css) var staff_css_tmpl = template.CSS(staff_css)
var groups map[int]Group = make(map[int]Group) var groups map[int]Group = make(map[int]Group)
@ -60,7 +64,7 @@ func init_database(err error) {
if(dbpassword != ""){ if(dbpassword != ""){
dbpassword = ":" + dbpassword dbpassword = ":" + dbpassword
} }
db, err = sql.Open("mysql",dbuser + dbpassword + "@tcp(" + dbhost + ":" + dbport + ")/" + dbname) db, err = sql.Open("mysql",dbuser + dbpassword + "@tcp(" + dbhost + ":" + dbport + ")/" + dbname + "?collation=utf8mb4_general_ci")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -177,7 +181,7 @@ func init_database(err error) {
// create_account_stmt, err = db.Prepare("INSERT INTO // create_account_stmt, err = db.Prepare("INSERT INTO
log.Print("Preparing register statement.") log.Print("Preparing register statement.")
register_stmt, err = db.Prepare("INSERT INTO users(`name`,`password`,`salt`,`group`,`is_super_admin`,`session`) VALUES(?,?,?,2,0,?)") register_stmt, err = db.Prepare("INSERT INTO users(`name`,`password`,`salt`,`group`,`is_super_admin`,`session`) VALUES(?,?,?," + strconv.Itoa(default_group) + ",0,?)")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -188,6 +192,12 @@ func init_database(err error) {
log.Fatal(err) log.Fatal(err)
} }
log.Print("Preparing change_group statement.")
change_group_stmt, err = db.Prepare("UPDATE `users` SET `group` = ? WHERE `uid` = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing create_profile_reply statement.") log.Print("Preparing create_profile_reply statement.")
create_profile_reply_stmt, err = db.Prepare("INSERT INTO users_replies(uid,content,parsed_content,createdAt,createdBy) VALUES(?,?,?,NOW(),?)") create_profile_reply_stmt, err = db.Prepare("INSERT INTO users_replies(uid,content,parsed_content,createdAt,createdBy) VALUES(?,?,?,NOW(),?)")
if err != nil { if err != nil {
@ -285,31 +295,31 @@ func main(){
var err error var err error
init_database(err); init_database(err);
log.Print("Loading the custom pages.")
err = filepath.Walk("pages/", add_custom_page)
if err != nil {
log.Fatal(err)
}
log.Print("Loading the static files.") log.Print("Loading the static files.")
files, err := ioutil.ReadDir("./public") err = filepath.Walk("./public", func(path string, f os.FileInfo, err error) error {
if err != nil {
log.Fatal(err)
}
for _, f := range files {
if f.IsDir() { if f.IsDir() {
continue return nil
}
data, err := ioutil.ReadFile("./public/" + f.Name())
if err != nil {
log.Fatal(err)
} }
log.Print("Added the '" + f.Name() + "' static file.") path = strings.Replace(path,"\\","/",-1)
static_files["/static/" + f.Name()] = SFile{data,0,int64(len(data)),mime.TypeByExtension(filepath.Ext(f.Name())),f,f.ModTime().UTC().Format(http.TimeFormat)} data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
path = strings.TrimPrefix(path,"public/")
log.Print("Added the '" + path + "' static file.")
static_files["/static/" + path] = SFile{data,0,int64(len(data)),mime.TypeByExtension(filepath.Ext("/public/" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}
return nil
})
if err != nil {
log.Fatal(err)
} }
//parse_map["grinning"] = []byte("😀")
//parse_map["grin"] = []byte("😁")
//parse_map["joy"] = []byte("😂")
// In a directory to stop it clashing with the other paths // In a directory to stop it clashing with the other paths
http.HandleFunc("/static/", route_static) http.HandleFunc("/static/", route_static)
//http.HandleFunc("/static/", route_fstatic) //http.HandleFunc("/static/", route_fstatic)
@ -359,7 +369,9 @@ func main(){
http.HandleFunc("/profile/reply/edit/submit/", route_profile_reply_edit_submit) http.HandleFunc("/profile/reply/edit/submit/", route_profile_reply_edit_submit)
http.HandleFunc("/profile/reply/delete/submit/", route_profile_reply_delete_submit) http.HandleFunc("/profile/reply/delete/submit/", route_profile_reply_delete_submit)
//http.HandleFunc("/user/:id/edit/", route_logout) //http.HandleFunc("/user/:id/edit/", route_logout)
//http.HandleFunc("/user/:id/ban/", route_logout) http.HandleFunc("/users/ban/", route_ban)
http.HandleFunc("/users/ban/submit/", route_ban_submit)
http.HandleFunc("/users/unban/", route_unban)
// Admin // Admin
http.HandleFunc("/panel/forums/", route_panel_forums) http.HandleFunc("/panel/forums/", route_panel_forums)

View File

@ -47,7 +47,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
} }
topic_content := html.EscapeString(r.PostFormValue("topic_content")) topic_content := html.EscapeString(r.PostFormValue("topic_content"))
_, err = edit_topic_stmt.Exec(topic_name, topic_content, parse_message(topic_content), is_closed, tid) _, err = edit_topic_stmt.Exec(topic_name, preparse_message(topic_content), parse_message(html.EscapeString(preparse_message(topic_content))), is_closed, tid)
if err != nil { if err != nil {
InternalErrorJSQ(err,w,r,user,is_js) InternalErrorJSQ(err,w,r,user,is_js)
return return
@ -160,7 +160,7 @@ func route_reply_edit_submit(w http.ResponseWriter, r *http.Request) {
return return
} }
content := html.EscapeString(r.PostFormValue("edit_item")) content := html.EscapeString(preparse_message(r.PostFormValue("edit_item")))
_, err = edit_reply_stmt.Exec(content, parse_message(content), rid) _, err = edit_reply_stmt.Exec(content, parse_message(content), rid)
if err != nil { if err != nil {
InternalError(err,w,r,user) InternalError(err,w,r,user)
@ -262,7 +262,7 @@ func route_profile_reply_edit_submit(w http.ResponseWriter, r *http.Request) {
return return
} }
content := html.EscapeString(r.PostFormValue("edit_item")) content := html.EscapeString(preparse_message(r.PostFormValue("edit_item")))
_, err = edit_profile_reply_stmt.Exec(content, parse_message(content), rid) _, err = edit_profile_reply_stmt.Exec(content, parse_message(content), rid)
if err != nil { if err != nil {
InternalError(err,w,r,user) InternalError(err,w,r,user)
@ -324,6 +324,127 @@ func route_profile_reply_delete_submit(w http.ResponseWriter, r *http.Request) {
} }
} }
func route_ban(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Mod && !user.Is_Admin {
NoPermissions(w,r,user)
return
}
uid, err := strconv.Atoi(r.URL.Path[len("/users/ban/"):])
if err != nil {
LocalError("The provided User ID is not a valid number.",w,r,user)
return
}
var uname string
err = db.QueryRow("SELECT name from users where uid = ?", uid).Scan(&uname)
if err == sql.ErrNoRows {
LocalError("The user you're trying to ban no longer exists.",w,r,user)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
confirm_msg := "Are you sure you want to ban '" + uname + "'?"
yousure := AreYouSure{"/users/ban/submit/" + strconv.Itoa(uid),confirm_msg}
pi := Page{"Ban User","ban-user",user,tList,yousure}
templates.ExecuteTemplate(w,"areyousure.html", pi)
}
func route_ban_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Mod && !user.Is_Admin {
NoPermissions(w,r,user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
}
uid, err := strconv.Atoi(r.URL.Path[len("/users/ban/submit/"):])
if err != nil {
LocalError("The provided User ID is not a valid number.",w,r,user)
return
}
var group int
var is_super_admin bool
err = db.QueryRow("SELECT `group`, `is_super_admin` from `users` where `uid` = ?", uid).Scan(&group, &is_super_admin)
if err == sql.ErrNoRows {
LocalError("The user you're trying to ban no longer exists.",w,r,user)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
if is_super_admin || groups[group].Is_Admin || groups[group].Is_Mod {
LocalError("You may not ban another staff member.",w,r,user)
return
}
if uid == user.ID {
LocalError("You may not ban yourself.",w,r,user)
return
}
if uid == -2 {
LocalError("You may not ban me. Fine, I will offer up some guidance unto thee. Come to my lair, young one. /arcane-tower/",w,r,user)
return
}
if groups[group].Is_Banned {
LocalError("The user you're trying to unban is already banned.",w,r,user)
return
}
_, err = change_group_stmt.Exec(4, uid)
if err != nil {
InternalError(err,w,r,user)
return
}
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
}
func route_unban(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Mod && !user.Is_Admin {
NoPermissions(w,r,user)
return
}
uid, err := strconv.Atoi(r.URL.Path[len("/users/unban/"):])
if err != nil {
LocalError("The provided User ID is not a valid number.",w,r,user)
return
}
var uname string
var group int
err = db.QueryRow("SELECT `name`, `group` from users where `uid` = ?", uid).Scan(&uname, &group)
if err == sql.ErrNoRows {
LocalError("The user you're trying to unban no longer exists.",w,r,user)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
if !groups[group].Is_Banned {
LocalError("The user you're trying to unban isn't banned.",w,r,user)
return
}
_, err = change_group_stmt.Exec(default_group, uid)
if err != nil {
InternalError(err,w,r,user)
return
}
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
}
func route_panel_forums(w http.ResponseWriter, r *http.Request){ func route_panel_forums(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r) user := SessionCheck(w,r)
if !user.Is_Admin { if !user.Is_Admin {

View File

@ -1,9 +1,6 @@
package main package main
import "strings" import "strings"
import "os" //import "regexp"
import "log"
import "io/ioutil"
import "path/filepath"
type Page struct type Page struct
{ {
@ -27,31 +24,55 @@ type AreYouSure struct
Message string Message string
} }
func add_custom_page(path string, f os.FileInfo, err error) error { func shortcode_to_unicode(msg string) string {
if err != nil { //re := regexp.MustCompile(":(.):")
return err msg = strings.Replace(msg,":grinning:","😀",-1)
} msg = strings.Replace(msg,":grin:","😁",-1)
msg = strings.Replace(msg,":joy:","😂",-1)
// Is this a directory..? msg = strings.Replace(msg,":rofl:","🤣",-1)
fileInfo, err := os.Stat(path) msg = strings.Replace(msg,":smiley:","😃",-1)
is_dir := fileInfo.IsDir() msg = strings.Replace(msg,":smile:","😄",-1)
if err != nil { msg = strings.Replace(msg,":sweat_smile:","😅",-1)
return err msg = strings.Replace(msg,":laughing:","😆",-1)
} msg = strings.Replace(msg,":satisfied:","😆",-1)
if is_dir { msg = strings.Replace(msg,":wink:","😉",-1)
return err msg = strings.Replace(msg,":blush:","😊",-1)
} msg = strings.Replace(msg,":yum:","😋",-1)
msg = strings.Replace(msg,":sunglasses:","😎",-1)
custom_page, err := ioutil.ReadFile(path) msg = strings.Replace(msg,":heart_eyes:","😍",-1)
if err != nil { msg = strings.Replace(msg,":kissing_heart:","😘",-1)
return err msg = strings.Replace(msg,":kissing:","😗",-1)
} msg = strings.Replace(msg,":kissing_smiling_eyes:","😙",-1)
log.Print("Loaded the '" + path + "' page.") msg = strings.Replace(msg,":kissing_closed_eyes:","😚",-1)
name := strings.TrimSuffix(path, filepath.Ext(path)) msg = strings.Replace(msg,":relaxed:","☺️",-1)
custom_pages[name] = string(custom_page) msg = strings.Replace(msg,":slight_smile:","🙂",-1)
return nil msg = strings.Replace(msg,":hugging:","🤗",-1)
msg = strings.Replace(msg,":thinking:","🤔",-1)
msg = strings.Replace(msg,":neutral_face:","😐",-1)
msg = strings.Replace(msg,":expressionless:","😑",-1)
msg = strings.Replace(msg,":no_mouth:","😶",-1)
msg = strings.Replace(msg,":rolling_eyes:","🙄",-1)
msg = strings.Replace(msg,":smirk:","😏",-1)
msg = strings.Replace(msg,":persevere:","😣",-1)
msg = strings.Replace(msg,":disappointed_relieved:","😥",-1)
msg = strings.Replace(msg,":open_mouth:","😮",-1)
msg = strings.Replace(msg,":zipper_mouth:","🤐",-1)
msg = strings.Replace(msg,":hushed:","😯",-1)
msg = strings.Replace(msg,":sleepy:","😪",-1)
msg = strings.Replace(msg,":tired_face:","😫",-1)
msg = strings.Replace(msg,":sleeping:","😴",-1)
msg = strings.Replace(msg,":relieved:","😌",-1)
msg = strings.Replace(msg,":nerd:","🤓",-1)
return strings.Replace(msg,":stuck_out_tongue:","😛",-1)
}
func preparse_message(msg string) string {
return shortcode_to_unicode(msg)
} }
func parse_message(msg string) string { func parse_message(msg string) string {
msg = strings.Replace(msg,":)","😀",-1)
msg = strings.Replace(msg,":D","😃",-1)
//msg = shortcode_to_unicode(msg)
return strings.Replace(msg,"\n","<br>",-1) return strings.Replace(msg,"\n","<br>",-1)
} }

View File

@ -1 +1,6 @@
<div class="rowitem">Testing</div> {{template "header.html" . }}
<div class="rowblock">
<div class="rowitem">{{.Title}}</div>
</div>
<div class="rowblock">Testing</div>
{{template "footer.html" . }}

View File

@ -0,0 +1,14 @@
The MIT License
Copyright (c) 2011-2012 by linyows <linyows@gmail.com>
Permission is hereby granted, freef charge, to any personbtaining a copyf this software and associated documentation files (the 'Software'),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copiesf the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copiesr substantial portionsf the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,99 @@
*NOTICE* This extends the [original plugin](https://github.com/diy/jquery-emojiarea) by groups. (See screenshot at dropdown menu)
#### About this extension
This was originally created for my project *The Msngr*, which should have become an open-source end-to-end encrypted messanger. I am always happy about an attribution to me and my website, e.g. [\<a href="https://pius-ladenburger.de">Pius Ladenburger\</a>](https://pius-ladenburger.de).
I hope this helps you and makes your day easier.
#.emojiarea()
A small **6kb** [jQuery](http://jquery.com/) plugin for turning regular textareas into ones that support emojis, WYSIWYG style! Set up a list of available emojis, call `$('textarea').emojiarea()` and you're done (basically). There's a plain-text fallback, so if the browser doesn't support [contentEditable](http://caniuse.com/#search=contenteditable), it will degrade gracefully—the user will still be able to use the dropdown menu of emojis.
![Screenshot](http://i.imgur.com/C4Z8F.gif)
```html
<textarea>Hello :smile:</textarea>
<script type="text/javascript">$('textarea').emojiarea();</script>
```
## Configuration
### Dropdown Menu
![Dropdown Screenshot](http://i.imgur.com/EuTTpHk.png)
By default, the plugin will insert a link after the editor that toggles the emoji selector when clicked.
```html
<a href="javascript:void(0)" class="emoji-button">Emojis</a>
```
If you wish change this behavior and have the button placed before the editor, or change the label of the link, use:
```javascript
$('textarea').emojiarea({
buttonLabel: 'Add Emoji',
buttonPosition: 'before'
});
```
Alternatively, if you wish to use your own button:
```javascript
$('textarea').emojiarea({button: '#your-button'});
```
For customizing the visual appearance, see the [CSS / Skinning](#css--skinning) section.
### Available Emojis
```javascript
$.emojiarea.path = '/path/to/folder/with/icons';
$.emojiarea.icons = {
':smile:' : 'smile.png',
':angry:' : 'angry.png',
':flushed:' : 'flushed.png',
':neckbeard:' : 'neckbeard.png',
':laughing:' : 'laughing.png'
};
```
### Defaults
If you wish to set the defaults for `$().emojiarea()`, extend `$.emojiarea.defaults` like so:
```javascript
$.extend($.emojiarea.defaults, {
buttonPosition: 'before'
});
```
For a basic set of emojis, see "packs/basic".
## CSS / Skinning
See [jquery.emojiarea.css](https://github.com/diy/jquery-emojiarea/blob/master/jquery.emojiarea.css) for the few fundamental CSS styles needed for this to work.
Basically, you'll want to adjust the following styles:
```css
.emoji-wysiwyg-editor /* the editor box itself */
.emoji-menu > div /* the dropdown menu with options */
.emoji-wysiwyg-editor img /* the emoji images in the editor */
```
## Footnotes
* Huge props to [Tim Down](http://stackoverflow.com/users/96100/tim-down) for the many insightful answers on Stack Overflow having to deal with cross-browser selection handling.
* If you have a really rad set of emojis and would like to share, please fork this, add them to "packs/", and submit a pull request!
* For a giant list of emojis (used by Github, Basecamp, et al), see ["Emoji cheat sheet"](http://www.emoji-cheat-sheet.com/).
## License
Copyright &copy; 2012 DIY Co and 2015 Pius Ladenburger
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.
I am always happy about an attribution to me and my website, e.g. [\<a href="https://pius-ladenburger.de">Pius Ladenburger\</a>](https://pius-ladenburger.de)

862
public/jquery-emojiarea/emojis.js vendored Normal file
View File

@ -0,0 +1,862 @@
$.emojiarea.path = '/static/smilies/emojiarea';
$.emojiarea.icons = [{"name" : "<i class='icon-smile'></i>", "icons" : {':bowtie:' : 'bowtie.png',
':smile:' : 'smile.png',
':laughing:' : 'laughing.png',
':blush:' : 'blush.png',
':smiley:' : 'smiley.png',
':relaxed:' : 'relaxed.png',
':smirk:' : 'smirk.png',
':heart_eyes:' : 'heart_eyes.png',
':kissing_heart:' : 'kissing_heart.png',
':kissing_closed_eyes:' : 'kissing_closed_eyes.png',
':flushed:' : 'flushed.png',
':relieved:' : 'relieved.png',
':satisfied:' : 'satisfied.png',
':grin:' : 'grin.png',
':wink:' : 'wink.png',
':stuck_out_tongue_winking_eye:' : 'stuck_out_tongue_winking_eye.png',
':stuck_out_tongue_closed_eyes:' : 'stuck_out_tongue_closed_eyes.png',
':grinning:' : 'grinning.png',
':kissing:' : 'kissing.png',
':kissing_smiling_eyes:' : 'kissing_smiling_eyes.png',
':stuck_out_tongue:' : 'stuck_out_tongue.png',
':sleeping:' : 'sleeping.png',
':worried:' : 'worried.png',
':frowning:' : 'frowning.png',
':anguished:' : 'anguished.png',
':open_mouth:' : 'open_mouth.png',
':grimacing:' : 'grimacing.png',
':confused:' : 'confused.png',
':hushed:' : 'hushed.png',
':expressionless:' : 'expressionless.png',
':unamused:' : 'unamused.png',
':sweat_smile:' : 'sweat_smile.png',
':sweat:' : 'sweat.png',
':weary:' : 'weary.png',
':pensive:' : 'pensive.png',
':disappointed:' : 'disappointed.png',
':confounded:' : 'confounded.png',
':fearful:' : 'fearful.png',
':cold_sweat:' : 'cold_sweat.png',
':persevere:' : 'persevere.png',
':joy:' : 'joy.png',
':astonished:' : 'astonished.png',
':scream:' : 'scream.png',
':neckbeard:' : 'neckbeard.png',
':tired_face:' : 'tired_face.png',
':angry:' : 'angry.png',
':rage:' : 'rage.png',
':triumph:' : 'triumph.png',
':sleepy:' : 'sleepy.png',
':yum:' : 'yum.png',
':mask:' : 'mask.png',
':sunglasses:' : 'sunglasses.png',
':dizzy_face:' : 'dizzy_face.png',
':imp:' : 'imp.png',
':smiling_imp:' : 'smiling_imp.png',
':neutral_face:' : 'neutral_face.png',
':no_mouth:' : 'no_mouth.png',
':innocent:' : 'innocent.png',
':alien:' : 'alien.png',
':yellow_heart:' : 'yellow_heart.png',
':blue_heart:' : 'blue_heart.png',
':purple_heart:' : 'purple_heart.png',
':heart:' : 'heart.png',
':green_heart:' : 'green_heart.png',
':broken_heart:' : 'broken_heart.png',
':heartbeat:' : 'heartbeat.png',
':heartpulse:' : 'heartpulse.png',
':two_hearts:' : 'two_hearts.png',
':revolving_hearts:' : 'revolving_hearts.png',
':cupid:' : 'cupid.png',
':sparkling_heart:' : 'sparkling_heart.png',
':sparkles:' : 'sparkles.png',
':star:' : 'star.png',
':star2:' : 'star2.png',
':dizzy:' : 'dizzy.png',
':boom:' : 'boom.png',
':collision:' : 'collision.png',
':anger:' : 'anger.png',
':exclamation:' : 'exclamation.png',
':question:' : 'question.png',
':grey_exclamation:' : 'grey_exclamation.png',
':grey_question:' : 'grey_question.png',
':zzz:' : 'zzz.png',
':dash:' : 'dash.png',
':sweat_drops:' : 'sweat_drops.png',
':notes:' : 'notes.png',
':musical_note:' : 'musical_note.png',
':fire:' : 'fire.png',
':hankey:' : 'hankey.png',
':poop:' : 'poop.png',
':shit:' : 'shit.png',
':+1:' : '+1.png',
':thumbsup:' : 'thumbsup.png',
':-1:' : '-1.png',
':thumbsdown:' : 'thumbsdown.png',
':ok_hand:' : 'ok_hand.png',
':punch:' : 'punch.png',
':facepunch:' : 'facepunch.png',
':fist:' : 'fist.png',
':v:' : 'v.png',
':wave:' : 'wave.png',
':hand:' : 'hand.png',
':raised_hand:' : 'raised_hand.png',
':open_hands:' : 'open_hands.png',
':point_up:' : 'point_up.png',
':point_down:' : 'point_down.png',
':point_left:' : 'point_left.png',
':point_right:' : 'point_right.png',
':raised_hands:' : 'raised_hands.png',
':pray:' : 'pray.png',
':point_up_2:' : 'point_up_2.png',
':clap:' : 'clap.png',
':muscle:' : 'muscle.png',
':metal:' : 'metal.png',
':walking:' : 'walking.png',
':runner:' : 'runner.png',
':running:' : 'running.png',
':couple:' : 'couple.png',
':family:' : 'family.png',
':two_men_holding_hands:' : 'two_men_holding_hands.png',
':two_women_holding_hands:' : 'two_women_holding_hands.png',
':ok_woman:' : 'ok_woman.png',
':no_good:' : 'no_good.png',
':information_desk_person:' : 'information_desk_person.png',
':bride_with_veil:' : 'bride_with_veil.png',
':person_with_pouting_face:' : 'person_with_pouting_face.png',
':person_frowning:' : 'person_frowning.png',
':bow:' : 'bow.png',
':couplekiss:' : 'couplekiss.png',
':couple_with_heart:' : 'couple_with_heart.png',
':massage:' : 'massage.png',
':haircut:' : 'haircut.png',
':nail_care:' : 'nail_care.png',
':boy:' : 'boy.png',
':girl:' : 'girl.png',
':woman:' : 'woman.png',
':man:' : 'man.png',
':baby:' : 'baby.png',
':older_woman:' : 'older_woman.png',
':older_man:' : 'older_man.png',
':person_with_blond_hair:' : 'person_with_blond_hair.png',
':man_with_gua_pi_mao:' : 'man_with_gua_pi_mao.png',
':man_with_turban:' : 'man_with_turban.png',
':construction_worker:' : 'construction_worker.png',
':cop:' : 'cop.png',
':angel:' : 'angel.png',
':princess:' : 'princess.png',
':smiley_cat:' : 'smiley_cat.png',
':smile_cat:' : 'smile_cat.png',
':heart_eyes_cat:' : 'heart_eyes_cat.png',
':kissing_cat:' : 'kissing_cat.png',
':smirk_cat:' : 'smirk_cat.png',
':scream_cat:' : 'scream_cat.png',
':crying_cat_face:' : 'crying_cat_face.png',
':joy_cat:' : 'joy_cat.png',
':pouting_cat:' : 'pouting_cat.png',
':japanese_ogre:' : 'japanese_ogre.png',
':japanese_goblin:' : 'japanese_goblin.png',
':see_no_evil:' : 'see_no_evil.png',
':hear_no_evil:' : 'hear_no_evil.png',
':speak_no_evil:' : 'speak_no_evil.png',
':guardsman:' : 'guardsman.png',
':skull:' : 'skull.png',
':feet:' : 'feet.png',
':lips:' : 'lips.png',
':kiss:' : 'kiss.png',
':droplet:' : 'droplet.png',
':ear:' : 'ear.png',
':eyes:' : 'eyes.png',
':nose:' : 'nose.png',
':tongue:' : 'tongue.png',
':love_letter:' : 'love_letter.png',
':bust_in_silhouette:' : 'bust_in_silhouette.png',
':busts_in_silhouette:' : 'busts_in_silhouette.png',
':speech_balloon:' : 'speech_balloon.png',
':thought_balloon:' : 'thought_balloon.png',
':feelsgood:' : 'feelsgood.png',
':finnadie:' : 'finnadie.png',
':goberserk:' : 'goberserk.png',
':godmode:' : 'godmode.png',
':hurtrealbad:' : 'hurtrealbad.png',
':rage1:' : 'rage1.png',
':rage2:' : 'rage2.png',
':rage3:' : 'rage3.png',
':rage4:' : 'rage4.png',
':suspect:' : 'suspect.png',
':trollface:' : 'trollface.png'}},
{'name': '<i class="icon-tree"></i>', 'icons' : {':sunny:' : 'sunny.png',
':umbrella:' : 'umbrella.png',
':cloud:' : 'cloud.png',
':snowflake:' : 'snowflake.png',
':snowman:' : 'snowman.png',
':zap:' : 'zap.png',
':cyclone:' : 'cyclone.png',
':foggy:' : 'foggy.png',
':ocean:' : 'ocean.png',
':cat:' : 'cat.png',
':dog:' : 'dog.png',
':mouse:' : 'mouse.png',
':hamster:' : 'hamster.png',
':rabbit:' : 'rabbit.png',
':wolf:' : 'wolf.png',
':frog:' : 'frog.png',
':tiger:' : 'tiger.png',
':koala:' : 'koala.png',
':bear:' : 'bear.png',
':pig:' : 'pig.png',
':pig_nose:' : 'pig_nose.png',
':cow:' : 'cow.png',
':boar:' : 'boar.png',
':monkey_face:' : 'monkey_face.png',
':monkey:' : 'monkey.png',
':horse:' : 'horse.png',
':racehorse:' : 'racehorse.png',
':camel:' : 'camel.png',
':sheep:' : 'sheep.png',
':elephant:' : 'elephant.png',
':panda_face:' : 'panda_face.png',
':snake:' : 'snake.png',
':bird:' : 'bird.png',
':baby_chick:' : 'baby_chick.png',
':hatched_chick:' : 'hatched_chick.png',
':hatching_chick:' : 'hatching_chick.png',
':chicken:' : 'chicken.png',
':penguin:' : 'penguin.png',
':turtle:' : 'turtle.png',
':bug:' : 'bug.png',
':honeybee:' : 'honeybee.png',
':ant:' : 'ant.png',
':beetle:' : 'beetle.png',
':snail:' : 'snail.png',
':octopus:' : 'octopus.png',
':tropical_fish:' : 'tropical_fish.png',
':fish:' : 'fish.png',
':whale:' : 'whale.png',
':whale2:' : 'whale2.png',
':dolphin:' : 'dolphin.png',
':cow2:' : 'cow2.png',
':ram:' : 'ram.png',
':rat:' : 'rat.png',
':water_buffalo:' : 'water_buffalo.png',
':tiger2:' : 'tiger2.png',
':rabbit2:' : 'rabbit2.png',
':dragon:' : 'dragon.png',
':goat:' : 'goat.png',
':rooster:' : 'rooster.png',
':dog2:' : 'dog2.png',
':pig2:' : 'pig2.png',
':mouse2:' : 'mouse2.png',
':ox:' : 'ox.png',
':dragon_face:' : 'dragon_face.png',
':blowfish:' : 'blowfish.png',
':crocodile:' : 'crocodile.png',
':dromedary_camel:' : 'dromedary_camel.png',
':leopard:' : 'leopard.png',
':cat2:' : 'cat2.png',
':poodle:' : 'poodle.png',
':paw_prints:' : 'paw_prints.png',
':bouquet:' : 'bouquet.png',
':cherry_blossom:' : 'cherry_blossom.png',
':tulip:' : 'tulip.png',
':four_leaf_clover:' : 'four_leaf_clover.png',
':rose:' : 'rose.png',
':sunflower:' : 'sunflower.png',
':hibiscus:' : 'hibiscus.png',
':maple_leaf:' : 'maple_leaf.png',
':leaves:' : 'leaves.png',
':fallen_leaf:' : 'fallen_leaf.png',
':herb:' : 'herb.png',
':mushroom:' : 'mushroom.png',
':cactus:' : 'cactus.png',
':palm_tree:' : 'palm_tree.png',
':evergreen_tree:' : 'evergreen_tree.png',
':deciduous_tree:' : 'deciduous_tree.png',
':chestnut:' : 'chestnut.png',
':seedling:' : 'seedling.png',
':blossom:' : 'blossom.png',
':ear_of_rice:' : 'ear_of_rice.png',
':shell:' : 'shell.png',
':globe_with_meridians:' : 'globe_with_meridians.png',
':sun_with_face:' : 'sun_with_face.png',
':full_moon_with_face:' : 'full_moon_with_face.png',
':new_moon_with_face:' : 'new_moon_with_face.png',
':new_moon:' : 'new_moon.png',
':waxing_crescent_moon:' : 'waxing_crescent_moon.png',
':first_quarter_moon:' : 'first_quarter_moon.png',
':waxing_gibbous_moon:' : 'waxing_gibbous_moon.png',
':full_moon:' : 'full_moon.png',
':waning_gibbous_moon:' : 'waning_gibbous_moon.png',
':last_quarter_moon:' : 'last_quarter_moon.png',
':waning_crescent_moon:' : 'waning_crescent_moon.png',
':last_quarter_moon_with_face:' : 'last_quarter_moon_with_face.png',
':first_quarter_moon_with_face:' : 'first_quarter_moon_with_face.png',
':moon:' : 'moon.png',
':earth_africa:' : 'earth_africa.png',
':earth_americas:' : 'earth_americas.png',
':earth_asia:' : 'earth_asia.png',
':volcano:' : 'volcano.png',
':milky_way:' : 'milky_way.png',
':partly_sunny:' : 'partly_sunny.png',
':octocat:' : 'octocat.png'}},
{'name': '<i class="icon-bell-alt"></i>', 'icons' : {':squirrel:' : 'squirrel.png',
':bamboo:' : 'bamboo.png',
':gift_heart:' : 'gift_heart.png',
':dolls:' : 'dolls.png',
':school_satchel:' : 'school_satchel.png',
':mortar_board:' : 'mortar_board.png',
':flags:' : 'flags.png',
':fireworks:' : 'fireworks.png',
':sparkler:' : 'sparkler.png',
':wind_chime:' : 'wind_chime.png',
':rice_scene:' : 'rice_scene.png',
':jack_o_lantern:' : 'jack_o_lantern.png',
':ghost:' : 'ghost.png',
':santa:' : 'santa.png',
':christmas_tree:' : 'christmas_tree.png',
':gift:' : 'gift.png',
':bell:' : 'bell.png',
':no_bell:' : 'no_bell.png',
':tanabata_tree:' : 'tanabata_tree.png',
':tada:' : 'tada.png',
':confetti_ball:' : 'confetti_ball.png',
':balloon:' : 'balloon.png',
':crystal_ball:' : 'crystal_ball.png',
':cd:' : 'cd.png',
':dvd:' : 'dvd.png',
':floppy_disk:' : 'floppy_disk.png',
':camera:' : 'camera.png',
':video_camera:' : 'video_camera.png',
':movie_camera:' : 'movie_camera.png',
':computer:' : 'computer.png',
':tv:' : 'tv.png',
':iphone:' : 'iphone.png',
':phone:' : 'phone.png',
':telephone:' : 'telephone.png',
':telephone_receiver:' : 'telephone_receiver.png',
':pager:' : 'pager.png',
':fax:' : 'fax.png',
':minidisc:' : 'minidisc.png',
':vhs:' : 'vhs.png',
':sound:' : 'sound.png',
':speaker:' : 'speaker.png',
':mute:' : 'mute.png',
':loudspeaker:' : 'loudspeaker.png',
':mega:' : 'mega.png',
':hourglass:' : 'hourglass.png',
':hourglass_flowing_sand:' : 'hourglass_flowing_sand.png',
':alarm_clock:' : 'alarm_clock.png',
':watch:' : 'watch.png',
':radio:' : 'radio.png',
':satellite:' : 'satellite.png',
':loop:' : 'loop.png',
':mag:' : 'mag.png',
':mag_right:' : 'mag_right.png',
':unlock:' : 'unlock.png',
':lock:' : 'lock.png',
':lock_with_ink_pen:' : 'lock_with_ink_pen.png',
':closed_lock_with_key:' : 'closed_lock_with_key.png',
':key:' : 'key.png',
':bulb:' : 'bulb.png',
':flashlight:' : 'flashlight.png',
':high_brightness:' : 'high_brightness.png',
':low_brightness:' : 'low_brightness.png',
':electric_plug:' : 'electric_plug.png',
':battery:' : 'battery.png',
':calling:' : 'calling.png',
':email:' : 'email.png',
':mailbox:' : 'mailbox.png',
':postbox:' : 'postbox.png',
':bath:' : 'bath.png',
':bathtub:' : 'bathtub.png',
':shower:' : 'shower.png',
':toilet:' : 'toilet.png',
':wrench:' : 'wrench.png',
':nut_and_bolt:' : 'nut_and_bolt.png',
':hammer:' : 'hammer.png',
':seat:' : 'seat.png',
':moneybag:' : 'moneybag.png',
':yen:' : 'yen.png',
':dollar:' : 'dollar.png',
':pound:' : 'pound.png',
':euro:' : 'euro.png',
':credit_card:' : 'credit_card.png',
':money_with_wings:' : 'money_with_wings.png',
':e-mail:' : 'e-mail.png',
':inbox_tray:' : 'inbox_tray.png',
':outbox_tray:' : 'outbox_tray.png',
':envelope:' : 'envelope.png',
':incoming_envelope:' : 'incoming_envelope.png',
':postal_horn:' : 'postal_horn.png',
':mailbox_closed:' : 'mailbox_closed.png',
':mailbox_with_mail:' : 'mailbox_with_mail.png',
':mailbox_with_no_mail:' : 'mailbox_with_no_mail.png',
':door:' : 'door.png',
':smoking:' : 'smoking.png',
':bomb:' : 'bomb.png',
':gun:' : 'gun.png',
':hocho:' : 'hocho.png',
':pill:' : 'pill.png',
':syringe:' : 'syringe.png',
':page_facing_up:' : 'page_facing_up.png',
':page_with_curl:' : 'page_with_curl.png',
':bookmark_tabs:' : 'bookmark_tabs.png',
':bar_chart:' : 'bar_chart.png',
':chart_with_upwards_trend:' : 'chart_with_upwards_trend.png',
':chart_with_downwards_trend:' : 'chart_with_downwards_trend.png',
':scroll:' : 'scroll.png',
':clipboard:' : 'clipboard.png',
':calendar:' : 'calendar.png',
':date:' : 'date.png',
':card_index:' : 'card_index.png',
':file_folder:' : 'file_folder.png',
':open_file_folder:' : 'open_file_folder.png',
':scissors:' : 'scissors.png',
':pushpin:' : 'pushpin.png',
':paperclip:' : 'paperclip.png',
':black_nib:' : 'black_nib.png',
':pencil2:' : 'pencil2.png',
':straight_ruler:' : 'straight_ruler.png',
':triangular_ruler:' : 'triangular_ruler.png',
':closed_book:' : 'closed_book.png',
':green_book:' : 'green_book.png',
':blue_book:' : 'blue_book.png',
':orange_book:' : 'orange_book.png',
':notebook:' : 'notebook.png',
':notebook_with_decorative_cover:' : 'notebook_with_decorative_cover.png',
':ledger:' : 'ledger.png',
':books:' : 'books.png',
':bookmark:' : 'bookmark.png',
':name_badge:' : 'name_badge.png',
':microscope:' : 'microscope.png',
':telescope:' : 'telescope.png',
':newspaper:' : 'newspaper.png',
':football:' : 'football.png',
':basketball:' : 'basketball.png',
':soccer:' : 'soccer.png',
':baseball:' : 'baseball.png',
':tennis:' : 'tennis.png',
':8ball:' : '8ball.png',
':rugby_football:' : 'rugby_football.png',
':bowling:' : 'bowling.png',
':golf:' : 'golf.png',
':mountain_bicyclist:' : 'mountain_bicyclist.png',
':bicyclist:' : 'bicyclist.png',
':horse_racing:' : 'horse_racing.png',
':snowboarder:' : 'snowboarder.png',
':swimmer:' : 'swimmer.png',
':surfer:' : 'surfer.png',
':ski:' : 'ski.png',
':spades:' : 'spades.png',
':hearts:' : 'hearts.png',
':clubs:' : 'clubs.png',
':diamonds:' : 'diamonds.png',
':gem:' : 'gem.png',
':ring:' : 'ring.png',
':trophy:' : 'trophy.png',
':musical_score:' : 'musical_score.png',
':musical_keyboard:' : 'musical_keyboard.png',
':violin:' : 'violin.png',
':space_invader:' : 'space_invader.png',
':video_game:' : 'video_game.png',
':black_joker:' : 'black_joker.png',
':flower_playing_cards:' : 'flower_playing_cards.png',
':game_die:' : 'game_die.png',
':dart:' : 'dart.png',
':mahjong:' : 'mahjong.png',
':clapper:' : 'clapper.png',
':memo:' : 'memo.png',
':pencil:' : 'pencil.png',
':book:' : 'book.png',
':art:' : 'art.png',
':microphone:' : 'microphone.png',
':headphones:' : 'headphones.png',
':trumpet:' : 'trumpet.png',
':saxophone:' : 'saxophone.png',
':guitar:' : 'guitar.png',
':shoe:' : 'shoe.png',
':sandal:' : 'sandal.png',
':high_heel:' : 'high_heel.png',
':lipstick:' : 'lipstick.png',
':boot:' : 'boot.png',
':shirt:' : 'shirt.png',
':tshirt:' : 'tshirt.png',
':necktie:' : 'necktie.png',
':womans_clothes:' : 'womans_clothes.png',
':dress:' : 'dress.png',
':running_shirt_with_sash:' : 'running_shirt_with_sash.png',
':jeans:' : 'jeans.png',
':kimono:' : 'kimono.png',
':bikini:' : 'bikini.png',
':ribbon:' : 'ribbon.png',
':tophat:' : 'tophat.png',
':crown:' : 'crown.png',
':womans_hat:' : 'womans_hat.png',
':mans_shoe:' : 'mans_shoe.png',
':closed_umbrella:' : 'closed_umbrella.png',
':briefcase:' : 'briefcase.png',
':handbag:' : 'handbag.png',
':pouch:' : 'pouch.png',
':purse:' : 'purse.png',
':eyeglasses:' : 'eyeglasses.png',
':fishing_pole_and_fish:' : 'fishing_pole_and_fish.png',
':coffee:' : 'coffee.png',
':tea:' : 'tea.png',
':sake:' : 'sake.png',
':baby_bottle:' : 'baby_bottle.png',
':beer:' : 'beer.png',
':beers:' : 'beers.png',
':cocktail:' : 'cocktail.png',
':tropical_drink:' : 'tropical_drink.png',
':wine_glass:' : 'wine_glass.png',
':fork_and_knife:' : 'fork_and_knife.png',
':pizza:' : 'pizza.png',
':hamburger:' : 'hamburger.png',
':fries:' : 'fries.png',
':poultry_leg:' : 'poultry_leg.png',
':meat_on_bone:' : 'meat_on_bone.png',
':spaghetti:' : 'spaghetti.png',
':curry:' : 'curry.png',
':fried_shrimp:' : 'fried_shrimp.png',
':bento:' : 'bento.png',
':sushi:' : 'sushi.png',
':fish_cake:' : 'fish_cake.png',
':rice_ball:' : 'rice_ball.png',
':rice_cracker:' : 'rice_cracker.png',
':rice:' : 'rice.png',
':ramen:' : 'ramen.png',
':stew:' : 'stew.png',
':oden:' : 'oden.png',
':dango:' : 'dango.png',
':egg:' : 'egg.png',
':bread:' : 'bread.png',
':doughnut:' : 'doughnut.png',
':custard:' : 'custard.png',
':icecream:' : 'icecream.png',
':ice_cream:' : 'ice_cream.png',
':shaved_ice:' : 'shaved_ice.png',
':birthday:' : 'birthday.png',
':cake:' : 'cake.png',
':cookie:' : 'cookie.png',
':chocolate_bar:' : 'chocolate_bar.png',
':candy:' : 'candy.png',
':lollipop:' : 'lollipop.png',
':honey_pot:' : 'honey_pot.png',
':apple:' : 'apple.png',
':green_apple:' : 'green_apple.png',
':tangerine:' : 'tangerine.png',
':lemon:' : 'lemon.png',
':cherries:' : 'cherries.png',
':grapes:' : 'grapes.png',
':watermelon:' : 'watermelon.png',
':strawberry:' : 'strawberry.png',
':peach:' : 'peach.png',
':melon:' : 'melon.png',
':banana:' : 'banana.png',
':pear:' : 'pear.png',
':pineapple:' : 'pineapple.png',
':sweet_potato:' : 'sweet_potato.png',
':eggplant:' : 'eggplant.png',
':tomato:' : 'tomato.png'}},
{'name': '<i class="icon-location"></i>', 'icons' : {':corn:' : 'corn.png',
':house:' : 'house.png',
':house_with_garden:' : 'house_with_garden.png',
':school:' : 'school.png',
':office:' : 'office.png',
':post_office:' : 'post_office.png',
':hospital:' : 'hospital.png',
':bank:' : 'bank.png',
':convenience_store:' : 'convenience_store.png',
':love_hotel:' : 'love_hotel.png',
':hotel:' : 'hotel.png',
':wedding:' : 'wedding.png',
':church:' : 'church.png',
':department_store:' : 'department_store.png',
':european_post_office:' : 'european_post_office.png',
':city_sunrise:' : 'city_sunrise.png',
':city_sunset:' : 'city_sunset.png',
':japanese_castle:' : 'japanese_castle.png',
':european_castle:' : 'european_castle.png',
':tent:' : 'tent.png',
':factory:' : 'factory.png',
':tokyo_tower:' : 'tokyo_tower.png',
':japan:' : 'japan.png',
':mount_fuji:' : 'mount_fuji.png',
':sunrise_over_mountains:' : 'sunrise_over_mountains.png',
':sunrise:' : 'sunrise.png',
':stars:' : 'stars.png',
':statue_of_liberty:' : 'statue_of_liberty.png',
':bridge_at_night:' : 'bridge_at_night.png',
':carousel_horse:' : 'carousel_horse.png',
':rainbow:' : 'rainbow.png',
':ferris_wheel:' : 'ferris_wheel.png',
':fountain:' : 'fountain.png',
':roller_coaster:' : 'roller_coaster.png',
':ship:' : 'ship.png',
':speedboat:' : 'speedboat.png',
':boat:' : 'boat.png',
':sailboat:' : 'sailboat.png',
':rowboat:' : 'rowboat.png',
':anchor:' : 'anchor.png',
':rocket:' : 'rocket.png',
':airplane:' : 'airplane.png',
':helicopter:' : 'helicopter.png',
':steam_locomotive:' : 'steam_locomotive.png',
':tram:' : 'tram.png',
':mountain_railway:' : 'mountain_railway.png',
':bike:' : 'bike.png',
':aerial_tramway:' : 'aerial_tramway.png',
':suspension_railway:' : 'suspension_railway.png',
':mountain_cableway:' : 'mountain_cableway.png',
':tractor:' : 'tractor.png',
':blue_car:' : 'blue_car.png',
':oncoming_automobile:' : 'oncoming_automobile.png',
':car:' : 'car.png',
':red_car:' : 'red_car.png',
':taxi:' : 'taxi.png',
':oncoming_taxi:' : 'oncoming_taxi.png',
':articulated_lorry:' : 'articulated_lorry.png',
':bus:' : 'bus.png',
':oncoming_bus:' : 'oncoming_bus.png',
':rotating_light:' : 'rotating_light.png',
':police_car:' : 'police_car.png',
':oncoming_police_car:' : 'oncoming_police_car.png',
':fire_engine:' : 'fire_engine.png',
':ambulance:' : 'ambulance.png',
':minibus:' : 'minibus.png',
':truck:' : 'truck.png',
':train:' : 'train.png',
':station:' : 'station.png',
':train2:' : 'train2.png',
':bullettrain_front:' : 'bullettrain_front.png',
':bullettrain_side:' : 'bullettrain_side.png',
':light_rail:' : 'light_rail.png',
':monorail:' : 'monorail.png',
':railway_car:' : 'railway_car.png',
':trolleybus:' : 'trolleybus.png',
':ticket:' : 'ticket.png',
':fuelpump:' : 'fuelpump.png',
':vertical_traffic_light:' : 'vertical_traffic_light.png',
':traffic_light:' : 'traffic_light.png',
':warning:' : 'warning.png',
':construction:' : 'construction.png',
':beginner:' : 'beginner.png',
':atm:' : 'atm.png',
':slot_machine:' : 'slot_machine.png',
':busstop:' : 'busstop.png',
':barber:' : 'barber.png',
':hotsprings:' : 'hotsprings.png',
':checkered_flag:' : 'checkered_flag.png',
':crossed_flags:' : 'crossed_flags.png',
':izakaya_lantern:' : 'izakaya_lantern.png',
':moyai:' : 'moyai.png',
':circus_tent:' : 'circus_tent.png',
':performing_arts:' : 'performing_arts.png',
':round_pushpin:' : 'round_pushpin.png',
':triangular_flag_on_post:' : 'triangular_flag_on_post.png',
':jp:' : 'jp.png',
':kr:' : 'kr.png',
':cn:' : 'cn.png',
':us:' : 'us.png',
':fr:' : 'fr.png',
':es:' : 'es.png',
':it:' : 'it.png',
':ru:' : 'ru.png',
':gb:' : 'gb.png',
':uk:' : 'uk.png',
':de:' : 'de.png'}},
{'name': '<i class="icon-dollar"></i>', 'icons' : {':one:' : 'one.png',
':two:' : 'two.png',
':three:' : 'three.png',
':four:' : 'four.png',
':five:' : 'five.png',
':six:' : 'six.png',
':seven:' : 'seven.png',
':eight:' : 'eight.png',
':nine:' : 'nine.png',
':keycap_ten:' : 'keycap_ten.png',
':1234:' : '1234.png',
':zero:' : 'zero.png',
':hash:' : 'hash.png',
':symbols:' : 'symbols.png',
':arrow_backward:' : 'arrow_backward.png',
':arrow_down:' : 'arrow_down.png',
':arrow_forward:' : 'arrow_forward.png',
':arrow_left:' : 'arrow_left.png',
':capital_abcd:' : 'capital_abcd.png',
':abcd:' : 'abcd.png',
':abc:' : 'abc.png',
':arrow_lower_left:' : 'arrow_lower_left.png',
':arrow_lower_right:' : 'arrow_lower_right.png',
':arrow_right:' : 'arrow_right.png',
':arrow_up:' : 'arrow_up.png',
':arrow_upper_left:' : 'arrow_upper_left.png',
':arrow_upper_right:' : 'arrow_upper_right.png',
':arrow_double_down:' : 'arrow_double_down.png',
':arrow_double_up:' : 'arrow_double_up.png',
':arrow_down_small:' : 'arrow_down_small.png',
':arrow_heading_down:' : 'arrow_heading_down.png',
':arrow_heading_up:' : 'arrow_heading_up.png',
':leftwards_arrow_with_hook:' : 'leftwards_arrow_with_hook.png',
':arrow_right_hook:' : 'arrow_right_hook.png',
':left_right_arrow:' : 'left_right_arrow.png',
':arrow_up_down:' : 'arrow_up_down.png',
':arrow_up_small:' : 'arrow_up_small.png',
':arrows_clockwise:' : 'arrows_clockwise.png',
':arrows_counterclockwise:' : 'arrows_counterclockwise.png',
':rewind:' : 'rewind.png',
':fast_forward:' : 'fast_forward.png',
':information_source:' : 'information_source.png',
':ok:' : 'ok.png',
':twisted_rightwards_arrows:' : 'twisted_rightwards_arrows.png',
':repeat:' : 'repeat.png',
':repeat_one:' : 'repeat_one.png',
':new:' : 'new.png',
':top:' : 'top.png',
':up:' : 'up.png',
':cool:' : 'cool.png',
':free:' : 'free.png',
':ng:' : 'ng.png',
':cinema:' : 'cinema.png',
':koko:' : 'koko.png',
':signal_strength:' : 'signal_strength.png',
':u5272:' : 'u5272.png',
':u5408:' : 'u5408.png',
':u55b6:' : 'u55b6.png',
':u6307:' : 'u6307.png',
':u6708:' : 'u6708.png',
':u6709:' : 'u6709.png',
':u6e80:' : 'u6e80.png',
':u7121:' : 'u7121.png',
':u7533:' : 'u7533.png',
':u7a7a:' : 'u7a7a.png',
':u7981:' : 'u7981.png',
':sa:' : 'sa.png',
':restroom:' : 'restroom.png',
':mens:' : 'mens.png',
':womens:' : 'womens.png',
':baby_symbol:' : 'baby_symbol.png',
':no_smoking:' : 'no_smoking.png',
':parking:' : 'parking.png',
':wheelchair:' : 'wheelchair.png',
':metro:' : 'metro.png',
':baggage_claim:' : 'baggage_claim.png',
':accept:' : 'accept.png',
':wc:' : 'wc.png',
':potable_water:' : 'potable_water.png',
':put_litter_in_its_place:' : 'put_litter_in_its_place.png',
':secret:' : 'secret.png',
':congratulations:' : 'congratulations.png',
':m:' : 'm.png',
':passport_control:' : 'passport_control.png',
':left_luggage:' : 'left_luggage.png',
':customs:' : 'customs.png',
':ideograph_advantage:' : 'ideograph_advantage.png',
':cl:' : 'cl.png',
':sos:' : 'sos.png',
':id:' : 'id.png',
':no_entry_sign:' : 'no_entry_sign.png',
':underage:' : 'underage.png',
':no_mobile_phones:' : 'no_mobile_phones.png',
':do_not_litter:' : 'do_not_litter.png',
':non-potable_water:' : 'non-potable_water.png',
':no_bicycles:' : 'no_bicycles.png',
':no_pedestrians:' : 'no_pedestrians.png',
':children_crossing:' : 'children_crossing.png',
':no_entry:' : 'no_entry.png',
':eight_spoked_asterisk:' : 'eight_spoked_asterisk.png',
':eight_pointed_black_star:' : 'eight_pointed_black_star.png',
':heart_decoration:' : 'heart_decoration.png',
':vs:' : 'vs.png',
':vibration_mode:' : 'vibration_mode.png',
':mobile_phone_off:' : 'mobile_phone_off.png',
':chart:' : 'chart.png',
':currency_exchange:' : 'currency_exchange.png',
':aries:' : 'aries.png',
':taurus:' : 'taurus.png',
':gemini:' : 'gemini.png',
':cancer:' : 'cancer.png',
':leo:' : 'leo.png',
':virgo:' : 'virgo.png',
':libra:' : 'libra.png',
':scorpius:' : 'scorpius.png',
':sagittarius:' : 'sagittarius.png',
':capricorn:' : 'capricorn.png',
':aquarius:' : 'aquarius.png',
':pisces:' : 'pisces.png',
':ophiuchus:' : 'ophiuchus.png',
':six_pointed_star:' : 'six_pointed_star.png',
':negative_squared_cross_mark:' : 'negative_squared_cross_mark.png',
':a:' : 'a.png',
':b:' : 'b.png',
':ab:' : 'ab.png',
':o2:' : 'o2.png',
':diamond_shape_with_a_dot_inside:' : 'diamond_shape_with_a_dot_inside.png',
':recycle:' : 'recycle.png',
':end:' : 'end.png',
':on:' : 'on.png',
':soon:' : 'soon.png',
':clock1:' : 'clock1.png',
':clock130:' : 'clock130.png',
':clock10:' : 'clock10.png',
':clock1030:' : 'clock1030.png',
':clock11:' : 'clock11.png',
':clock1130:' : 'clock1130.png',
':clock12:' : 'clock12.png',
':clock1230:' : 'clock1230.png',
':clock2:' : 'clock2.png',
':clock230:' : 'clock230.png',
':clock3:' : 'clock3.png',
':clock330:' : 'clock330.png',
':clock4:' : 'clock4.png',
':clock430:' : 'clock430.png',
':clock5:' : 'clock5.png',
':clock530:' : 'clock530.png',
':clock6:' : 'clock6.png',
':clock630:' : 'clock630.png',
':clock7:' : 'clock7.png',
':clock730:' : 'clock730.png',
':clock8:' : 'clock8.png',
':clock830:' : 'clock830.png',
':clock9:' : 'clock9.png',
':clock930:' : 'clock930.png',
':heavy_dollar_sign:' : 'heavy_dollar_sign.png',
':copyright:' : 'copyright.png',
':registered:' : 'registered.png',
':tm:' : 'tm.png',
':x:' : 'x.png',
':heavy_exclamation_mark:' : 'heavy_exclamation_mark.png',
':bangbang:' : 'bangbang.png',
':interrobang:' : 'interrobang.png',
':o:' : 'o.png',
':heavy_multiplication_x:' : 'heavy_multiplication_x.png',
':heavy_plus_sign:' : 'heavy_plus_sign.png',
':heavy_minus_sign:' : 'heavy_minus_sign.png',
':heavy_division_sign:' : 'heavy_division_sign.png',
':white_flower:' : 'white_flower.png',
':100:' : '100.png',
':heavy_check_mark:' : 'heavy_check_mark.png',
':ballot_box_with_check:' : 'ballot_box_with_check.png',
':radio_button:' : 'radio_button.png',
':link:' : 'link.png',
':curly_loop:' : 'curly_loop.png',
':wavy_dash:' : 'wavy_dash.png',
':part_alternation_mark:' : 'part_alternation_mark.png',
':trident:' : 'trident.png',
':black_square:' : 'black_square.png',
':white_check_mark:' : 'white_check_mark.png',
':black_square_button:' : 'black_square_button.png',
':white_square_button:' : 'white_square_button.png',
':black_circle:' : 'black_circle.png',
':white_circle:' : 'white_circle.png',
':red_circle:' : 'red_circle.png',
':large_blue_circle:' : 'large_blue_circle.png',
':large_blue_diamond:' : 'large_blue_diamond.png',
':large_orange_diamond:' : 'large_orange_diamond.png',
':small_blue_diamond:' : 'small_blue_diamond.png',
':small_orange_diamond:' : 'small_orange_diamond.png',
':small_red_triangle:' : 'small_red_triangle.png',
':small_red_triangle_down:' : 'small_red_triangle_down.png',
':shipit:' : 'shipit.png',
}}, ];

View File

@ -0,0 +1,97 @@
.emoji-wysiwyg-editor {
border: 1px solid #d0d0d0;
overflow: auto;
outline: none;
}
.emoji-wysiwyg-editor img {
width: 20px;
height: 20px;
vertical-align: middle;
margin: -3px 0 0 0;
}
.emoji-menu {
position: absolute;
z-index: 999;
width: 180px;
margin-left: -100px;
padding: 0;
overflow: hidden;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.emoji-menu > div {
max-height: 200px;
overflow: hidden;
background: #fff;
width: 200px;
-webkit-overflow-scrolling: touch;
overflow: auto;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.3);
-moz-box-shadow: 0 1px 5px rgba(0,0,0,0.3);
box-shadow: 0 1px 5px rgba(0,0,0,0.3);
padding-top: 40px;
}
.emoji-menu img {
width: 25px;
height: 25px;
vertical-align: middle;
border: 0 none;
}
.emoji-menu a {
margin: -1px 0 0 -1px;
border: 1px solid #f2f2f2;
padding: 5px;
display: block;
float: left;
}
.emoji-menu a:hover {
background-color: #fffae7;
}
.emoji-menu:after {
content: ' ';
display: block;
clear: left;
}
.emoji-menu a .label {
display: none;
}
.emoji-menu div {
overflow-x: hidden;
overflow-y: auto;
}
.emoji-menu .group-selector {
position: absolute;
list-style-type: none;
height: 40px;
top: 0;
left: 0;
width: 100%;
background-color: rgb(255,255,255);
background-color: rgba(255,255,255, .9);
}
.emoji-menu .group-selector li {
height: 15px;
width: 17px;
padding: 5px;
}
.emoji-menu .group-selector a:last-child li {
width: 15px;
}
.emoji-menu .group-selector a {
color: #EB7878;
text-decoration: none;
border: none;
background-color: transparent;
}
.emoji-menu .group-selector a:hover, .emoji-menu .group-selector a.active {
color:#000000;
}

View File

@ -0,0 +1,485 @@
/**
* emojiarea - A rich textarea control that supports emojis, WYSIWYG-style.
* Copyright (c) 2012 DIY Co
*
* 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.
*
* @author Brian Reavis <brian@diy.org>
*/
(function($, window, document) {
var ELEMENT_NODE = 1;
var TEXT_NODE = 3;
var TAGS_BLOCK = ['p', 'div', 'pre', 'form'];
var KEY_ESC = 27;
var KEY_TAB = 9;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
$.emojiarea = {
path: '',
icons: {},
defaults: {
button: null,
buttonLabel: 'Emojis',
buttonPosition: 'after'
}
};
$.fn.emojiarea = function(options) {
options = $.extend({}, $.emojiarea.defaults, options);
return this.each(function() {
var $textarea = $(this);
if ('contentEditable' in document.body && options.wysiwyg !== false) {
new EmojiArea_WYSIWYG($textarea, options);
} else {
new EmojiArea_Plain($textarea, options);
}
});
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var util = {};
util.restoreSelection = (function() {
if (window.getSelection) {
return function(savedSelection) {
var sel = window.getSelection();
sel.removeAllRanges();
for (var i = 0, len = savedSelection.length; i < len; ++i) {
sel.addRange(savedSelection[i]);
}
};
} else if (document.selection && document.selection.createRange) {
return function(savedSelection) {
if (savedSelection) {
savedSelection.select();
}
};
}
})();
util.saveSelection = (function() {
if (window.getSelection) {
return function() {
var sel = window.getSelection(), ranges = [];
if (sel.rangeCount) {
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
ranges.push(sel.getRangeAt(i));
}
}
return ranges;
};
} else if (document.selection && document.selection.createRange) {
return function() {
var sel = document.selection;
return (sel.type.toLowerCase() !== 'none') ? sel.createRange() : null;
};
}
})();
util.replaceSelection = (function() {
if (window.getSelection) {
return function(content) {
var range, sel = window.getSelection();
var node = typeof content === 'string' ? document.createTextNode(content) : content;
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(' '));
range.insertNode(node);
range.setStart(node, 0);
window.setTimeout(function() {
range = document.createRange();
range.setStartAfter(node);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}, 0);
}
}
} else if (document.selection && document.selection.createRange) {
return function(content) {
var range = document.selection.createRange();
if (typeof content === 'string') {
range.text = content;
} else {
range.pasteHTML(content.outerHTML);
}
}
}
})();
util.insertAtCursor = function(text, el) {
text = ' ' + text;
var val = el.value, endIndex, startIndex, range;
if (typeof el.selectionStart != 'undefined' && typeof el.selectionEnd != 'undefined') {
startIndex = el.selectionStart;
endIndex = el.selectionEnd;
el.value = val.substring(0, startIndex) + text + val.substring(el.selectionEnd);
el.selectionStart = el.selectionEnd = startIndex + text.length;
} else if (typeof document.selection != 'undefined' && typeof document.selection.createRange != 'undefined') {
el.focus();
range = document.selection.createRange();
range.text = text;
range.select();
}
};
util.extend = function(a, b) {
if (typeof a === 'undefined' || !a) { a = {}; }
if (typeof b === 'object') {
for (var key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
}
return a;
};
util.escapeRegex = function(str) {
return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
};
util.htmlEntities = function(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var EmojiArea = function() {};
EmojiArea.prototype.setup = function() {
var self = this;
this.$editor.on('focus', function() { self.hasFocus = true; });
this.$editor.on('blur', function() { self.hasFocus = false; });
this.setupButton();
};
EmojiArea.prototype.setupButton = function() {
var self = this;
var $button;
if (this.options.button) {
$button = $(this.options.button);
} else if (this.options.button !== false) {
$button = $('<a href="javascript:void(0)">');
$button.html(this.options.buttonLabel);
$button.addClass('emoji-button');
$button.attr({title: this.options.buttonLabel});
this.$editor[this.options.buttonPosition]($button);
} else {
$button = $('');
}
$button.on('click', function(e) {
EmojiMenu.show(self);
e.stopPropagation();
});
this.$button = $button;
};
EmojiArea.createIcon = function(group, emoji) {
var filename = $.emojiarea.icons[group]['icons'][emoji];
var path = $.emojiarea.path || '';
if (path.length && path.charAt(path.length - 1) !== '/') {
path += '/';
}
return '<img src="' + path + filename + '" alt="' + util.htmlEntities(emoji) + '">';
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
* Editor (plain-text)
*
* @constructor
* @param {object} $textarea
* @param {object} options
*/
var EmojiArea_Plain = function($textarea, options) {
this.options = options;
this.$textarea = $textarea;
this.$editor = $textarea;
this.setup();
};
EmojiArea_Plain.prototype.insert = function(group, emoji) {
if (!$.emojiarea.icons[group]['icons'].hasOwnProperty(emoji)) return;
util.insertAtCursor(emoji, this.$textarea[0]);
this.$textarea.trigger('change');
};
EmojiArea_Plain.prototype.val = function() {
return this.$textarea.val();
};
util.extend(EmojiArea_Plain.prototype, EmojiArea.prototype);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
* Editor (rich)
*
* @constructor
* @param {object} $textarea
* @param {object} options
*/
var EmojiArea_WYSIWYG = function($textarea, options) {
var self = this;
this.options = options;
this.$textarea = $textarea;
this.$editor = $('<div>').addClass('emoji-wysiwyg-editor');
this.$editor.text($textarea.val());
this.$editor.attr({contenteditable: 'true'});
this.$editor.on('blur keyup paste', function() { return self.onChange.apply(self, arguments); });
this.$editor.on('mousedown focus', function() { document.execCommand('enableObjectResizing', false, false); });
this.$editor.on('blur', function() { document.execCommand('enableObjectResizing', true, true); });
var html = this.$editor.text();
var emojis = $.emojiarea.icons;
for (var group in emojis) {
for (var key in emojis[group]['icons']) {
if (emojis[group]['icons'].hasOwnProperty(key)) {
html = html.replace(new RegExp(util.escapeRegex(key), 'g'), EmojiArea.createIcon(group, key));
}
}
}
this.$editor.html(html);
$textarea.hide().after(this.$editor);
this.setup();
this.$button.on('mousedown', function() {
if (self.hasFocus) {
self.selection = util.saveSelection();
}
});
};
EmojiArea_WYSIWYG.prototype.onChange = function() {
this.$textarea.val(this.val()).trigger('change');
};
EmojiArea_WYSIWYG.prototype.insert = function(group, emoji) {
var content;
var $img = $(EmojiArea.createIcon(group, emoji));
if ($img[0].attachEvent) {
$img[0].attachEvent('onresizestart', function(e) { e.returnValue = false; }, false);
}
this.$editor.trigger('focus');
if (this.selection) {
util.restoreSelection(this.selection);
}
try { util.replaceSelection($img[0]); } catch (e) {}
this.onChange();
};
EmojiArea_WYSIWYG.prototype.val = function() {
var lines = [];
var line = [];
var flush = function() {
lines.push(line.join(''));
line = [];
};
var sanitizeNode = function(node) {
if (node.nodeType === TEXT_NODE) {
line.push(node.nodeValue);
} else if (node.nodeType === ELEMENT_NODE) {
var tagName = node.tagName.toLowerCase();
var isBlock = TAGS_BLOCK.indexOf(tagName) !== -1;
if (isBlock && line.length) flush();
if (tagName === 'img') {
var alt = node.getAttribute('alt') || '';
if (alt) line.push(alt);
return;
} else if (tagName === 'br') {
flush();
}
var children = node.childNodes;
for (var i = 0; i < children.length; i++) {
sanitizeNode(children[i]);
}
if (isBlock && line.length) flush();
}
};
var children = this.$editor[0].childNodes;
for (var i = 0; i < children.length; i++) {
sanitizeNode(children[i]);
}
if (line.length) flush();
return lines.join('\n');
};
util.extend(EmojiArea_WYSIWYG.prototype, EmojiArea.prototype);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
* Emoji Dropdown Menu
*
* @constructor
* @param {object} emojiarea
*/
var EmojiMenu = function() {
var self = this;
var $body = $(document.body);
var $window = $(window);
this.visible = false;
this.emojiarea = null;
this.$menu = $('<div>');
this.$menu.addClass('emoji-menu');
this.$menu.hide();
this.$items = $('<div>').appendTo(this.$menu);
$body.append(this.$menu);
$body.on('keydown', function(e) {
if (e.keyCode === KEY_ESC || e.keyCode === KEY_TAB) {
self.hide();
}
});
$body.on('mouseup', function() {
self.hide();
});
$window.on('resize', function() {
if (self.visible) self.reposition();
});
this.$menu.on('mouseup', 'a', function(e) {
e.stopPropagation();
return false;
});
this.$menu.on('click', 'a', function(e) {
var emoji = $('.label', $(this)).text();
var group = $('.label', $(this)).parent().parent().attr('group');
if(group && emoji !== ''){
window.setTimeout(function() {
self.onItemSelected.apply(self, [group, emoji]);
}, 0);
e.stopPropagation();
return false;
}
});
this.load();
};
EmojiMenu.prototype.onItemSelected = function(group, emoji) {
this.emojiarea.insert(group, emoji);
this.hide();
};
EmojiMenu.prototype.load = function() {
var html = [];
var groups = [];
var options = $.emojiarea.icons;
var path = $.emojiarea.path;
if (path.length && path.charAt(path.length - 1) !== '/') {
path += '/';
}
groups.push('<ul class="group-selector">');
for (var group in options) {
groups.push('<a href="#group_' + group + '" class="tab_switch"><li>' + options[group]['name'] + '</li></a>');
html.push('<div class="select_group" group="' + group + '" id="group_' + group + '">');
for (var key in options[group]['icons']) {
if (options[group]['icons'].hasOwnProperty(key)) {
var filename = options[key];
html.push('<a href="javascript:void(0)" title="' + util.htmlEntities(key) + '">' + EmojiArea.createIcon(group, key) + '<span class="label">' + util.htmlEntities(key) + '</span></a>');
}
}
html.push('</div>');
}
groups.push('</ul>');
this.$items.html(html.join(''));
this.$menu.prepend(groups.join(''));
this.$menu.find('.tab_switch').each(function(i) {
if (i != 0) {
var select = $(this).attr('href');
$(select).hide();
} else {
$(this).addClass('active');
}
$(this).click(function() {
$(this).addClass('active');
$(this).siblings().removeClass('active');
$('.select_group').hide();
var select = $(this).attr('href');
$(select).show();
});
});
};
EmojiMenu.prototype.reposition = function() {
var $button = this.emojiarea.$button;
var offset = $button.offset();
offset.top += $button.outerHeight();
offset.left += Math.round($button.outerWidth() / 2);
this.$menu.css({
top: offset.top,
left: offset.left
});
};
EmojiMenu.prototype.hide = function(callback) {
if (this.emojiarea) {
this.emojiarea.menu = null;
this.emojiarea.$button.removeClass('on');
this.emojiarea = null;
}
this.visible = false;
this.$menu.hide();
};
EmojiMenu.prototype.show = function(emojiarea) {
if (this.emojiarea && this.emojiarea === emojiarea) return;
this.emojiarea = emojiarea;
this.emojiarea.menu = this;
this.reposition();
this.$menu.show();
this.visible = true;
};
EmojiMenu.show = (function() {
var menu = null;
return function(emojiarea) {
menu = menu || new EmojiMenu();
menu.show(emojiarea);
};
})();
})(jQuery, window, document);

File diff suppressed because one or more lines are too long

View File

@ -9,6 +9,25 @@ body
font-family: arial; font-family: arial;
} }
@font-face {
font-family: 'EmojiFont';
src: url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff2') format('woff2'),
url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff') format('woff'), local("arial");
}
@supports (-ms-ime-align:auto) {
.user_content
{
font-family: EmojiFont, arial;
}
}
@-moz-document url-prefix() {
.user_content
{
font-family: EmojiFont, arial;
}
}
ul ul
{ {
border: 1px solid #ccc; border: 1px solid #ccc;

View File

@ -54,11 +54,9 @@ func route_custom_page(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r) user := SessionCheck(w,r)
name := r.URL.Path[len("/pages/"):] name := r.URL.Path[len("/pages/"):]
val, ok := custom_pages[name]; pi := Page{"Page","page",user,tList,0}
if ok { err := custom_pages.ExecuteTemplate(w,name, pi)
pi := Page{"Page","page",user,tList,val} if err != nil {
templates.ExecuteTemplate(w,"custom_page.html", pi)
} else {
NotFound(w,r,user) NotFound(w,r,user)
} }
} }
@ -407,6 +405,10 @@ func route_profile(w http.ResponseWriter, r *http.Request){
puser.Is_Admin = puser.Is_Super_Admin || groups[puser.Group].Is_Admin puser.Is_Admin = puser.Is_Super_Admin || groups[puser.Group].Is_Admin
puser.Is_Super_Mod = puser.Is_Admin || groups[puser.Group].Is_Mod puser.Is_Super_Mod = puser.Is_Admin || groups[puser.Group].Is_Mod
puser.Is_Mod = puser.Is_Super_Mod puser.Is_Mod = puser.Is_Super_Mod
puser.Is_Banned = groups[puser.Group].Is_Banned
if puser.Is_Banned && puser.Is_Super_Mod {
puser.Is_Banned = false
}
} }
if puser.Avatar != "" { if puser.Avatar != "" {
@ -500,7 +502,7 @@ func route_create_topic(w http.ResponseWriter, r *http.Request) {
success := 1 success := 1
topic_name := html.EscapeString(r.PostFormValue("topic-name")) topic_name := html.EscapeString(r.PostFormValue("topic-name"))
res, err := create_topic_stmt.Exec(topic_name,html.EscapeString(r.PostFormValue("topic-content")),parse_message(html.EscapeString(r.PostFormValue("topic-content"))),user.ID) res, err := create_topic_stmt.Exec(topic_name,html.EscapeString(preparse_message(r.PostFormValue("topic-content"))),parse_message(html.EscapeString(preparse_message(r.PostFormValue("topic-content")))),user.ID)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
success = 0 success = 0
@ -566,7 +568,9 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return return
} }
_, err = create_reply_stmt.Exec(tid,html.EscapeString(r.PostFormValue("reply-content")),parse_message(html.EscapeString(r.PostFormValue("reply-content"))),user.ID) content := preparse_message(html.EscapeString(r.PostFormValue("reply-content")))
log.Print(content)
_, err = create_reply_stmt.Exec(tid,content,parse_message(content),user.ID)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
success = 0 success = 0
@ -636,7 +640,7 @@ func route_profile_reply_create(w http.ResponseWriter, r *http.Request) {
return return
} }
_, err = create_profile_reply_stmt.Exec(uid,html.EscapeString(r.PostFormValue("reply-content")),parse_message(html.EscapeString(r.PostFormValue("reply-content"))),user.ID) _, err = create_profile_reply_stmt.Exec(uid,html.EscapeString(preparse_message(r.PostFormValue("reply-content"))),parse_message(html.EscapeString(preparse_message(r.PostFormValue("reply-content")))),user.ID)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
success = 0 success = 0

View File

@ -4,7 +4,9 @@
<div class="rowitem">{{.Something.Name}}</div> <div class="rowitem">{{.Something.Name}}</div>
<div class="rowitem passive"> <div class="rowitem passive">
<a class="username">Add Friend</a> <a class="username">Add Friend</a>
{{if (.CurrentUser.Is_Super_Mod) and not (.Something.Is_Super_Mod) }}<a class="username">Ban</a>{{end}} {{if (.CurrentUser.Is_Super_Mod) and not (.Something.Is_Super_Mod) }}
{{if .Something.Is_Banned }}<a href="/users/unban/{{.Something.ID}}" class="username">Unban</a>{{else}}<a href="/users/ban/{{.Something.ID}}" class="username">Ban</a>{{end}}
{{end}}
<a class="username">Report</a> <a class="username">Report</a>
</div> </div>
</div> </div>
@ -14,7 +16,7 @@
<div class="colblock_right" style="overflow: hidden;"> <div class="colblock_right" style="overflow: hidden;">
{{range $index, $element := .ItemList}} {{range $index, $element := .ItemList}}
<div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le $element.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{$element.Css}}{{end}}"> <div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le $element.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{$element.Css}}{{end}}">
<span class="editable_block">{{$element.ContentHtml}}</span> <span class="editable_block user_content">{{$element.ContentHtml}}</span>
<br /><br /> <br /><br />
<a href="/user/{{$element.CreatedBy}}" class="username">{{$element.CreatedByName}}</a> <a href="/user/{{$element.CreatedBy}}" class="username">{{$element.CreatedByName}}</a>
{{if $.CurrentUser.Is_Mod}}<a href="/profile/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a> {{if $.CurrentUser.Is_Mod}}<a href="/profile/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a>
@ -31,7 +33,7 @@
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div> <div class="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div>
</div> </div>
<div class="formrow"> <div class="formrow">
<div class="formitem"><button name="reply-button" class="formbutton">Create Reply</div></div> <div class="formitem"><button name="reply-button" class="formbutton">Create Reply</button></div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -1,7 +1,7 @@
{{template "header.html" . }} {{template "header.html" . }}
<div class="rowblock"> <div class="rowblock">
<form action='/topic/edit/submit/{{.Something.ID}}' method="post"> <form action='/topic/edit/submit/{{.Something.ID}}' method="post">
<div class="rowitem"{{ if .Something.Sticky }}style="background-color: #FFFFEA;"{{end}}> <div class="rowitem"{{ if .Something.Sticky }} style="background-color: #FFFFEA;"{{end}}>
<a class='topic_name hide_on_edit'>{{.Something.Title}}</a> <a class='topic_name hide_on_edit'>{{.Something.Title}}</a>
<span class='topic_status topic_status_e topic_status_{{.Something.Status}} hide_on_edit'>{{.Something.Status}}</span> <span class='topic_status topic_status_e topic_status_{{.Something.Status}} hide_on_edit'>{{.Something.Status}}</span>
{{if .CurrentUser.Is_Mod}} {{if .CurrentUser.Is_Mod}}
@ -22,7 +22,7 @@
</div> </div>
<div class="rowblock"> <div class="rowblock">
<div class="rowitem passive editable_parent" style="border-bottom: none;{{ if .Something.Avatar }}background-image: url({{ .Something.Avatar }}), url(/static/white-dot.jpg);background-position: 0px {{if le .Something.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{.Something.Css}}{{end}}"> <div class="rowitem passive editable_parent" style="border-bottom: none;{{ if .Something.Avatar }}background-image: url({{ .Something.Avatar }}), url(/static/white-dot.jpg);background-position: 0px {{if le .Something.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{.Something.Css}}{{end}}">
<span class="hide_on_edit topic_content">{{.Something.Content}}</span> <span class="hide_on_edit topic_content user_content">{{.Something.Content}}</span>
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Something.Content}}</textarea> <textarea name="topic_content" class="show_on_edit topic_content_input">{{.Something.Content}}</textarea>
<br /><br /> <br /><br />
<a href="/user/{{.Something.CreatedBy}}" class="username">{{.Something.CreatedByName}}</a> <a href="/user/{{.Something.CreatedBy}}" class="username">{{.Something.CreatedByName}}</a>
@ -32,7 +32,7 @@
<div class="rowblock" style="overflow: hidden;"> <div class="rowblock" style="overflow: hidden;">
{{range $index, $element := .ItemList}} {{range $index, $element := .ItemList}}
<div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le $element.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{$element.Css}}{{end}}"> <div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le $element.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{$element.Css}}{{end}}">
<span class="editable_block">{{$element.ContentHtml}}</span> <span class="editable_block user_content">{{$element.ContentHtml}}</span>
<br /><br /> <br /><br />
<a href="/user/{{$element.CreatedBy}}" class="username">{{$element.CreatedByName}}</a> <a href="/user/{{$element.CreatedBy}}" class="username">{{$element.CreatedByName}}</a>
{{if $.CurrentUser.Is_Mod}}<a href="/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a> {{if $.CurrentUser.Is_Mod}}<a href="/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a>
@ -49,7 +49,7 @@
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div> <div class="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div>
</div> </div>
<div class="formrow"> <div class="formrow">
<div class="formitem"><button name="reply-button" class="formbutton">Create Reply</div></div> <div class="formitem"><button name="reply-button" class="formbutton">Create Reply</button></div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -71,10 +71,15 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (User) {
log.Print(err) log.Print(err)
return user return user
} }
user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin
user.Is_Super_Mod = groups[user.Group].Is_Mod || user.Is_Admin user.Is_Super_Mod = groups[user.Group].Is_Mod || user.Is_Admin
user.Is_Mod = user.Is_Super_Mod user.Is_Mod = user.Is_Super_Mod
user.Is_Banned = groups[user.Group].Is_Banned user.Is_Banned = groups[user.Group].Is_Banned
if user.Is_Banned && user.Is_Super_Mod {
user.Is_Banned = false
}
if user.Avatar != "" { if user.Avatar != "" {
if user.Avatar[0] == '.' { if user.Avatar[0] == '.' {
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar