208 Commits

Author SHA1 Message Date
a7337fa918 Work progress on dark theme 2019-01-09 11:40:00 +01:00
da047a976a Improved dark theme 2019-01-09 09:49:02 +01:00
379826f16b Work progress on dark theme 2019-01-08 20:55:40 +01:00
5dc64492cb Reorganized dark theme file 2019-01-08 20:28:41 +01:00
5ac5101ced Start to work on dark theme 2019-01-08 20:07:38 +01:00
f91ffba2a4 Display the number of responses for a choice on survey choices. 2018-08-26 15:10:03 +02:00
affe9a96f5 Fix typo 2018-08-26 14:01:06 +02:00
44fa0bea24 Handles too many account creation request error. 2018-08-20 14:51:02 +02:00
e77b509202 Handle already existing email account error. 2018-08-20 14:16:46 +02:00
06b43732a9 Handle "too many request" login errors. 2018-08-20 13:55:05 +02:00
ee2689ad84 Reduce too long conversation names. 2018-08-16 12:37:03 +02:00
8eadb03d59 Updated language picker. 2018-08-15 08:55:15 +02:00
6e0b3a4345 Added language settings 2018-08-15 08:51:02 +02:00
c633ce13b5 Save language in user account settings. 2018-08-15 08:26:14 +02:00
40593e00ed Automatically detect appropriated language 2018-08-15 07:52:05 +02:00
e214573e4e Add missing translation. 2018-08-15 07:45:58 +02:00
0825aba022 Fix typo 2018-08-15 07:44:34 +02:00
7a65263873 Added french translation 2018-08-09 12:30:22 +02:00
97f476e416 Fix bad translation 2018-08-09 11:51:38 +02:00
3dce138811 Fix error 2018-08-07 11:02:25 +02:00
28edb3122f Completed posts component translation. 2018-08-05 16:43:23 +02:00
2cd9338417 Added translation for dates 2018-08-05 16:40:14 +02:00
0af31052b4 Translated messages script. 2018-08-05 16:33:39 +02:00
81ba811622 Added comments component translation. 2018-08-05 16:29:49 +02:00
4b7008b5ba Fix typo. 2018-08-05 16:21:59 +02:00
02744d5c72 Translated post UI. 2018-08-05 16:13:48 +02:00
52c963cf1b Translated notifications dropdown. 2018-08-04 10:02:12 +02:00
52d1328f93 Translated conversations dropdown. 2018-08-04 09:58:40 +02:00
50b40ac5b0 Completed authenticated bar translation. 2018-08-04 09:53:35 +02:00
e1c3b0c002 Added an attribute to createElem2 to ease translation. 2018-08-04 09:51:01 +02:00
f859abcb2f Translated friends bar. 2018-08-04 09:37:36 +02:00
6e08208fed Translated like button. 2018-08-04 09:35:28 +02:00
391c74df62 Translated friendship status section. 2018-08-04 09:32:40 +02:00
5f2deebfb5 Translated access forbidden page. 2018-08-04 09:28:28 +02:00
6fa2123d57 Translated user page posts section. 2018-08-04 09:24:46 +02:00
536570abf1 Translated profile infos about user section. 2018-08-04 09:21:49 +02:00
ad67f6d852 Added translation to post form 2018-08-04 09:15:02 +02:00
ac6539a82c Added missing translation in post form. 2018-08-04 09:09:02 +02:00
1e1021fd49 Added translation for lastest posts page. 2018-08-04 09:03:31 +02:00
499aefbc1d Improved site responsive 2018-08-03 15:27:56 +02:00
c973b33ac8 Completed login page translation. 2018-08-03 15:14:53 +02:00
3397ddbf44 Completed home page translation. 2018-08-03 15:13:22 +02:00
607d2fafca Translated account created page. 2018-08-03 15:11:42 +02:00
ec7abaaf14 Translated create account page. 2018-08-03 15:07:59 +02:00
a389d731c2 Started to 2018-08-03 14:51:22 +02:00
4d7a99aeea Change page title 2018-08-02 09:43:24 +02:00
04b80782dc Support group membership notifications 2018-08-02 09:23:38 +02:00
837bc35a6b Improved search form responsive mode. 2018-08-01 06:44:01 +02:00
c98683e573 Improved groups page responsiveness. 2018-08-01 06:33:02 +02:00
f6eea9ebf8 Use iCheck flat checkboxes only. 2018-08-01 06:22:24 +02:00
7e99e8b14b Fix issue. 2018-07-31 13:55:58 +02:00
95b4c5fbc7 Created search page. 2018-07-31 13:53:57 +02:00
a50cca62d3 Fix bad comment. 2018-07-29 17:17:54 +02:00
f122da1306 Close search form dropdown on click on "see more results" 2018-07-29 17:12:52 +02:00
0692a689f1 Upgraded search component to include groups suppport. 2018-07-29 17:11:38 +02:00
fc8abc6d56 Upgraded search form component structure. 2018-07-29 08:08:23 +02:00
547daf4741 Ask to include groups posts in latest posts thread. 2018-07-20 15:17:26 +02:00
476f1b24a7 Improved posts UI 2018-07-20 14:27:49 +02:00
693b1fa26c Improved the way the name of the users who created the posts are shown. 2018-07-20 14:22:27 +02:00
655432a500 Fixed typo 2018-07-20 13:55:58 +02:00
bd888f0f47 Can display notifications related to groups posts. 2018-07-20 13:51:13 +02:00
ede515f7a2 Fix responsive issue 2018-07-20 09:23:32 +02:00
fa24c2d090 Fixed space 2018-07-20 09:17:06 +02:00
1522248793 Updated year notice 2018-07-20 09:16:50 +02:00
cc1e4dd22a Can get information about multiple groups and cache them. 2018-07-20 09:07:00 +02:00
61a20e8708 Added follow block. 2018-07-19 14:34:19 +02:00
a5815272c8 Fix bug 2018-07-17 10:27:32 +02:00
f66aba031c Display likes block. 2018-07-17 10:23:35 +02:00
059fcc72b6 Handles new kind of visibility level : members 2018-07-17 09:58:04 +02:00
acb4e1b384 Can scroll posts list to get older posts. 2018-07-17 09:40:13 +02:00
74e06a2b89 Display the posts of a group 2018-07-17 09:23:44 +02:00
2032017b0c Created new shorcut to open groups quickly 2018-07-16 15:13:17 +02:00
500633862e It is not possible to create private posts for groups. 2018-07-16 13:48:35 +02:00
57306d6872 Fix error 2018-07-16 13:30:20 +02:00
31bbc2326c Goups pages dynamically updated the title of the page. 2018-07-16 09:32:03 +02:00
5c0cdced85 Display posts creation form on groups pages. 2018-07-16 09:13:51 +02:00
07af7c961d Fix typo 2018-07-16 09:00:35 +02:00
ddd54e05e5 Can request update of posts creation level of the group. 2018-07-16 08:48:47 +02:00
1fb1e62fb6 Display url and description information in group header 2018-07-16 08:27:31 +02:00
80630dae58 Settings now includes group description and URL 2018-07-15 19:05:10 +02:00
85c117ff81 Updated licence 2018-07-15 18:37:56 +02:00
a62d2a495e Display group tag 2018-07-15 18:37:10 +02:00
51c248e463 Upgraded openUserPage function 2018-07-14 14:18:21 +02:00
665cbb8374 Virtual directory are handled at website's root directories. 2018-07-14 14:07:27 +02:00
5cfee77bb2 Added a live notice to check the availability of a virtual directory for a group. 2018-07-14 11:48:59 +02:00
a8e686d59b Can update the virtual directory of a group 2018-07-14 11:09:14 +02:00
6111e527e0 Added Travis configuration. 2018-07-12 13:50:33 +02:00
1b9ccfc95b Can request a membership deletion on the API 2018-07-10 14:48:16 +02:00
59c917f599 Display the list of groups of a user 2018-07-10 14:35:44 +02:00
b67f3b055f Can request membership level update. 2018-07-10 08:15:44 +02:00
1d88b8ea69 Can cancel groups membership invitations. 2018-07-09 15:57:51 +02:00
d541506820 Can respond to membership requests 2018-07-09 15:41:48 +02:00
c9d0a597f5 Can request membership deletion on the server. 2018-07-07 15:56:13 +02:00
e805dda4fb Created groups members table. 2018-07-07 15:27:27 +02:00
0be49b0d35 Made group registration level and visibility public 2018-07-07 07:36:03 +02:00
0c20b97b2f Improved header appearance 2018-07-07 07:27:55 +02:00
d32c1428cf Send group membreship requests to the server. 2018-07-06 18:48:49 +02:00
f90dc5d9ae Fix mistake 2018-07-06 18:43:14 +02:00
89df9f0315 Send a request to the server to cancel membership request. 2018-07-06 07:09:24 +02:00
561a3e9342 Can respond to group invitations. 2018-07-05 13:37:56 +02:00
66392491b5 Fix issue on signed out site. 2018-07-05 13:17:33 +02:00
a100d4a403 Display when the user is a moderator, a administrator or a member of a group. 2018-07-05 08:38:52 +02:00
9866298f19 Improved settings appearance 2018-07-05 08:10:42 +02:00
3bda514879 Can update group registration level. 2018-07-05 08:03:45 +02:00
7db41a9183 Display basic information about a group when access to it is forbidden. 2018-07-05 07:43:03 +02:00
dfce597139 Can update group visibility 2018-07-04 16:20:00 +02:00
24357aeb05 Fixed verb 2018-07-04 14:21:58 +02:00
a438eb81f0 Can generate a random group logo 2018-07-04 14:19:43 +02:00
1d92a5bf1c Can send a request on the API to delete group logo. 2018-07-04 14:07:16 +02:00
eb69734a38 Send a new group logo to the server. 2018-07-04 13:27:07 +02:00
6239d43f05 Added backward link 2018-07-04 11:39:33 +02:00
4c3985fc24 Send request to update group settings. 2018-07-04 11:26:18 +02:00
218e9b6fbe Retrieve group settings on the API 2018-07-04 06:05:14 +02:00
30696174f2 Added basic group information 2018-07-03 13:06:55 +02:00
a3ddd525d0 Updated comments creation request 2018-07-03 12:32:44 +02:00
2b8a7f08cb Fixed issue 2018-07-03 12:12:40 +02:00
e28b99f40c Basic groups header 2018-07-03 11:45:57 +02:00
b527b3a22d Fix error 2018-07-03 11:39:53 +02:00
0b5e50aade Fix build issue 2018-07-03 11:38:43 +02:00
dd53a39c4b Javascript errors are reported 2018-07-03 08:32:55 +02:00
f4e1b47d9d Improved groups creation form 2018-07-02 08:57:41 +02:00
28cb1e22e7 Send a request to the server to create the group 2018-07-02 08:42:55 +02:00
7ff7bceca3 Created groups main page 2018-07-02 08:07:59 +02:00
074b724d68 Made URLs detection more efficient 2018-07-02 07:39:04 +02:00
1ae92eeac8 Updated parser. 2018-05-27 12:10:42 +02:00
30fd40ad72 Do not show password reset form in case of error. 2018-05-27 11:12:16 +02:00
972232b370 Optimized code 2018-05-26 16:21:18 +02:00
2e0b130ac5 Optimize latest posts page 2018-05-26 16:19:24 +02:00
2c07e76684 Reduce traffic 2018-05-26 16:16:32 +02:00
9325cdd68b Made password reset available 2018-05-26 16:07:01 +02:00
19797b8731 Offer the user to reset his password 2018-05-26 15:50:33 +02:00
4ea1761c72 Fix too long URLs on error page 2018-05-26 07:32:32 +02:00
e0d5e50357 Security questions form now lives 2018-05-26 07:30:02 +02:00
f5638c3196 Can precise a time end to countdown timers 2018-05-24 21:22:50 +02:00
fa1c1596d5 Get the list of security questions of the user 2018-05-23 21:41:02 +02:00
bde2b5dde6 Made the option "contact admin" lives 2018-05-23 21:14:54 +02:00
91f8edd2de Create <p> tags quickly 2018-05-23 21:14:38 +02:00
9a7c3245c4 Prompt user preferred option to reset its password 2018-05-22 22:48:48 +02:00
79da14e0bc Added link to privacy policy on settings page 2018-05-22 22:23:16 +02:00
683a8eb0e6 Prompt user email on password reset 2018-05-21 10:40:44 +02:00
b0c4971838 Create empty password forgotten page 2018-05-21 10:05:32 +02:00
d01b9f4803 Page reload only in case of error 412 2018-05-20 15:01:25 +02:00
f4d019046d Updated welcome message 2018-05-20 14:48:16 +02:00
f41ac3b107 Added conversation page to top menu 2018-05-20 14:39:55 +02:00
8777832600 Dynamically resize conversation page boxes 2018-05-20 14:31:46 +02:00
8458c6190d Fix links appearance issue 2018-05-19 11:19:39 +02:00
725871dab1 Fix online config issue 2018-05-19 10:44:19 +02:00
d687e9962e Added Comunic.io build configuration 2018-05-19 10:25:42 +02:00
7abe97c1c2 Updated beta configuration 2018-05-19 10:22:24 +02:00
cfa5be5612 Can force connection to the website in https 2018-05-19 10:22:09 +02:00
b679b7e651 Display only unread conversations in bold 2018-05-16 17:56:59 +02:00
2fc2b0d641 Fix long words issue 2018-05-16 17:56:48 +02:00
c2b8177792 Improved conversation selection 2018-05-16 17:52:42 +02:00
b9cc47f05d Split refresh list method and create box method 2018-05-16 07:00:06 +02:00
ec13b6902b Current conversation is highlited 2018-05-16 06:47:41 +02:00
19576cebed Conversations page need login 2018-05-16 06:40:26 +02:00
ebd2fcf4f5 Conversations page admit conversation number as subfolder 2018-05-15 21:34:02 +02:00
73dfc842af Site current URI can now be easily updated. 2018-05-15 21:32:02 +02:00
1f636b7a5f Fix security issue 2018-05-15 21:15:09 +02:00
4a20815619 Can load older messages on conversations page 2018-05-15 19:17:23 +02:00
bf00a52430 Work progress on conversation page 2018-05-14 20:29:44 +02:00
bca2e6449e Apply user information on messages 2018-05-14 20:04:03 +02:00
46912f47c7 Basic display of conversation messages 2018-05-14 19:17:28 +02:00
7a473d1aa8 Can query the server to refresh only a single conversation. 2018-05-14 17:23:33 +02:00
92c7c80fbc Fix space issue 2018-05-14 17:04:21 +02:00
701d070efd Fix iOS 9 issue 2018-05-14 16:58:23 +02:00
4ec6213877 User data are included in data archive 2018-05-14 16:53:11 +02:00
e06a658422 Installed JSZip Utils 2018-05-14 16:25:49 +02:00
d086f9dfab Generate a ZIP file with orig JSON file 2018-05-13 21:27:46 +02:00
8f668c1bfa Added FileSaver 2018-05-13 21:08:34 +02:00
88308778d7 Can determine the list of files to download 2018-05-13 20:59:33 +02:00
7db7ecf69a Added JSZip 2018-05-13 20:29:07 +02:00
f447f30c6c Send request to get all account data 2018-05-13 16:52:19 +02:00
e9b6df8536 Pause conversation feature development 2018-05-13 15:51:49 +02:00
e32a7c67e1 Can send messages from the conversation page 2018-05-13 15:40:19 +02:00
e376c789a6 Display the list of conversations 2018-05-13 14:36:40 +02:00
1d9ae0dadd Created conversations page 2018-05-13 14:06:32 +02:00
1a922704ed URLs can be parsed 2018-05-13 12:16:32 +02:00
7a3387b550 Fix issue with tagging and emails 2018-05-13 11:59:54 +02:00
5e29916f97 Added OpenGraph information to the main page 2018-05-12 15:48:40 +02:00
d0ae8d0620 Fix iCheck issue on built version 2018-05-12 15:44:46 +02:00
4ef2e7f219 Added new link to about website in the footer 2018-05-12 12:40:46 +02:00
b412214976 Made easier to leave the website 2018-05-12 12:34:39 +02:00
1e299b6d5e Updated home page 2018-05-12 12:26:04 +02:00
5c5110a7cf User can generate random image 2018-05-12 10:06:56 +02:00
84df7adc89 Can generate random itentiy image 2018-05-12 09:51:25 +02:00
7ccfec5845 Made latest posts accessible for URI / 2018-05-11 08:03:32 +02:00
b69811105f Enlarge list of latest posts 2018-05-11 08:02:04 +02:00
925e0b11e8 Set a maximal length to comment input 2018-05-11 07:51:32 +02:00
9126a08d2f Added a link on home page to about website 2018-05-10 15:17:49 +02:00
4383efa32c Users must accept terms of use of the network 2018-05-10 15:06:29 +02:00
688a9de2b1 Added About Website URL (offline) 2018-05-10 15:00:10 +02:00
2c260c7174 Fix typo 2018-05-10 10:54:32 +02:00
42c934b723 Translated home page 2018-05-10 10:54:00 +02:00
cb2a7a5b04 Made translation of templates easier 2018-05-10 10:48:59 +02:00
28097368e1 Completed login page translation 2018-05-10 10:39:10 +02:00
877d9b40f5 Updated Docker build configuration 2018-05-09 18:37:44 +02:00
aefc20198c Created online configuration 2018-05-09 18:36:53 +02:00
4ac1b15520 Fix fonts issue 2018-05-09 18:29:42 +02:00
b457786ae9 Fix Chrome issue 2018-05-09 16:13:37 +02:00
d6413887c2 Can send request to delete user accounts 2018-05-09 14:57:46 +02:00
e55614b4c8 Fix close issue 2018-05-09 14:07:58 +02:00
53edcd8ffc Created privacy settings section 2018-05-09 12:18:28 +02:00
99f86060a4 Fix iOS 9 issue 2018-05-09 12:05:11 +02:00
0c68b8dd5d Fix iCheck issue with builder 2018-05-08 18:10:53 +02:00
11d5bdf752 Fix ionicons with builder 2018-05-08 18:06:35 +02:00
27401c9070 Create Docker build configuration 2018-05-08 12:45:46 +02:00
beafe2580a Fix issue 2018-05-08 12:39:16 +02:00
167 changed files with 21444 additions and 434 deletions

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
sudo: required
language: php
services:
- docker
#Target PHP versions
php:
- 7.2
script:
- docker run -t -v $(pwd):/data pierre42100/docker-comunicwebappscratch /data/builder build offline

View File

@ -1,5 +1,5 @@
MIT License MIT License
Copyright (c) <year> <copyright holders> Copyright (c) 2017-2018 Pierre HUBERT
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of 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 copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -20,4 +20,9 @@ ComunicWeb would not exists without the following technologies developped by the
- VideoJS - VideoJS
- jquery.hotkeys - jquery.hotkeys
- bootstrap-wysiwyg (https://github.com/steveathon/bootstrap-wysiwyg/) - bootstrap-wysiwyg (https://github.com/steveathon/bootstrap-wysiwyg/)
- wdt-emoji-bundle (http://ned.im/wdt-emoji-bundle) - wdt-emoji-bundle (http://ned.im/wdt-emoji-bundle)
- PNGLib (http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/) (BSD Licence)
- Identicon (http://github.com/stewartlord/identicon.js) (BSD Licence)
- FileSaver.js (http://eligrey.com) (by Eli Grey) (MIT Licence)
- JSZip (https://github.com/Stuk/jszip.git) (MIT Licence)
- JSZip Utils (https://github.com/Stuk/jszip-utils.git) (MIT Licence)

247
assets/3rdparty/FileSaver.js vendored Normal file
View File

@ -0,0 +1,247 @@
/*! FileSaver.js
* A saveAs() FileSaver implementation.
* 2014-01-24
*
* By Eli Grey, http://eligrey.com
* License: X11/MIT
* See LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs
// IE 10+ (native saveAs)
|| (typeof navigator !== "undefined" &&
navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator))
// Everyone else
|| (function(view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof navigator !== "undefined" &&
/MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case BlobBuilder.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, URL = view.URL || view.webkitURL || view
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = !view.externalHost && "download" in save_link
, click = function(node) {
var event = doc.createEvent("MouseEvents");
event.initMouseEvent(
"click", true, false, view, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
node.dispatchEvent(event);
}
, webkit_req_fs = view.webkitRequestFileSystem
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
, throw_outside = function(ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
, fs_min_size = 0
, deletion_queue = []
, process_deletion_queue = function() {
var i = deletion_queue.length;
while (i--) {
var file = deletion_queue[i];
if (typeof file === "string") { // file is an object URL
URL.revokeObjectURL(file);
} else { // file is a File
file.remove();
}
}
deletion_queue.length = 0; // clear queue
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, FileSaver = function(blob, name) {
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, blob_changed = false
, object_url
, target_view
, get_object_url = function() {
var object_url = get_URL().createObjectURL(blob);
deletion_queue.push(object_url);
return object_url;
}
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
// don't create more object URLs than needed
if (blob_changed || !object_url) {
object_url = get_object_url(blob);
}
if (target_view) {
target_view.location.href = object_url;
} else {
window.open(object_url, "_blank");
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
}
, abortable = function(func) {
return function() {
if (filesaver.readyState !== filesaver.DONE) {
return func.apply(this, arguments);
}
};
}
, create_if_not_found = {create: true, exclusive: false}
, slice
;
filesaver.readyState = filesaver.INIT;
if (!name) {
name = "download";
}
if (can_use_save_link) {
object_url = get_object_url(blob);
// FF for Android has a nasty garbage collection mechanism
// that turns all objects that are not pure javascript into 'deadObject'
// this means `doc` and `save_link` are unusable and need to be recreated
// `view` is usable though:
doc = view.document;
save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a");
save_link.href = object_url;
save_link.download = name;
var event = doc.createEvent("MouseEvents");
event.initMouseEvent(
"click", true, false, view, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
save_link.dispatchEvent(event);
filesaver.readyState = filesaver.DONE;
dispatch_all();
return;
}
// Object and web filesystem URLs have a problem saving in Google Chrome when
// viewed in a tab, so I force save with application/octet-stream
// http://code.google.com/p/chromium/issues/detail?id=91158
if (view.chrome && type && type !== force_saveable_type) {
slice = blob.slice || blob.webkitSlice;
blob = slice.call(blob, 0, blob.size, force_saveable_type);
blob_changed = true;
}
// Since I can't be sure that the guessed media type will trigger a download
// in WebKit, I append .download to the filename.
// https://bugs.webkit.org/show_bug.cgi?id=65440
if (webkit_req_fs && name !== "download") {
name += ".download";
}
if (type === force_saveable_type || webkit_req_fs) {
target_view = view;
}
if (!req_fs) {
fs_error();
return;
}
fs_min_size += blob.size;
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
var save = function() {
dir.getFile(name, create_if_not_found, abortable(function(file) {
file.createWriter(abortable(function(writer) {
writer.onwriteend = function(event) {
target_view.location.href = file.toURL();
deletion_queue.push(file);
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "writeend", event);
};
writer.onerror = function() {
var error = writer.error;
if (error.code !== error.ABORT_ERR) {
fs_error();
}
};
"writestart progress write abort".split(" ").forEach(function(event) {
writer["on" + event] = filesaver["on" + event];
});
writer.write(blob);
filesaver.abort = function() {
writer.abort();
filesaver.readyState = filesaver.DONE;
};
filesaver.readyState = filesaver.WRITING;
}), fs_error);
}), fs_error);
};
dir.getFile(name, {create: false}, abortable(function(file) {
// delete file if it already exists
file.remove();
save();
}), abortable(function(ex) {
if (ex.code === ex.NOT_FOUND_ERR) {
save();
} else {
fs_error();
}
}));
}), fs_error);
}), fs_error);
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name) {
return new FileSaver(blob, name);
}
;
FS_proto.abort = function() {
var filesaver = this;
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "abort");
};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
view.addEventListener("unload", process_deletion_queue, false);
saveAs.unload = function() {
process_deletion_queue();
view.removeEventListener("unload", process_deletion_queue, false);
};
return saveAs;
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined") module.exports = saveAs;

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(flat.png) no-repeat; background: url(icheck-flat-imgs/flat.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat, .icheckbox_flat,
.iradio_flat { .iradio_flat {
background-image: url(flat@2x.png); background-image: url(icheck-flat-imgs/flat@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -65,7 +65,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(red.png) no-repeat; background: url(icheck-flat-imgs/red.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -105,7 +105,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-red, .icheckbox_flat-red,
.iradio_flat-red { .iradio_flat-red {
background-image: url(red@2x.png); background-image: url(icheck-flat-imgs/red@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -121,7 +121,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(green.png) no-repeat; background: url(icheck-flat-imgs/green.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -161,7 +161,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-green, .icheckbox_flat-green,
.iradio_flat-green { .iradio_flat-green {
background-image: url(green@2x.png); background-image: url(icheck-flat-imgs/green@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -177,7 +177,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(blue.png) no-repeat; background: url(icheck-flat-imgs/blue.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -217,7 +217,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-blue, .icheckbox_flat-blue,
.iradio_flat-blue { .iradio_flat-blue {
background-image: url(blue@2x.png); background-image: url(icheck-flat-imgs/blue@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -233,7 +233,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(aero.png) no-repeat; background: url(icheck-flat-imgs/aero.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -273,7 +273,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-aero, .icheckbox_flat-aero,
.iradio_flat-aero { .iradio_flat-aero {
background-image: url(aero@2x.png); background-image: url(icheck-flat-imgs/aero@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -289,7 +289,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(grey.png) no-repeat; background: url(icheck-flat-imgs/grey.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -329,7 +329,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-grey, .icheckbox_flat-grey,
.iradio_flat-grey { .iradio_flat-grey {
background-image: url(grey@2x.png); background-image: url(icheck-flat-imgs/grey@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -345,7 +345,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(orange.png) no-repeat; background: url(icheck-flat-imgs/orange.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -385,7 +385,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-orange, .icheckbox_flat-orange,
.iradio_flat-orange { .iradio_flat-orange {
background-image: url(orange@2x.png); background-image: url(icheck-flat-imgs/orange@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -401,7 +401,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(yellow.png) no-repeat; background: url(icheck-flat-imgs/yellow.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -441,7 +441,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-yellow, .icheckbox_flat-yellow,
.iradio_flat-yellow { .iradio_flat-yellow {
background-image: url(yellow@2x.png); background-image: url(icheck-flat-imgs/yellow@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -457,7 +457,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(pink.png) no-repeat; background: url(icheck-flat-imgs/pink.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -497,7 +497,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-pink, .icheckbox_flat-pink,
.iradio_flat-pink { .iradio_flat-pink {
background-image: url(pink@2x.png); background-image: url(icheck-flat-imgs/pink@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }
@ -513,7 +513,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(purple.png) no-repeat; background: url(icheck-flat-imgs/purple.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -553,7 +553,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-purple, .icheckbox_flat-purple,
.iradio_flat-purple { .iradio_flat-purple {
background-image: url(purple@2x.png); background-image: url(icheck-flat-imgs/purple@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(aero.png) no-repeat; background: url(icheck-flat-imgs/aero.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-aero, .icheckbox_flat-aero,
.iradio_flat-aero { .iradio_flat-aero {
background-image: url(aero@2x.png); background-image: url(icheck-flat-imgs/aero@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(blue.png) no-repeat; background: url(icheck-flat-imgs/blue.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-blue, .icheckbox_flat-blue,
.iradio_flat-blue { .iradio_flat-blue {
background-image: url(blue@2x.png); background-image: url(icheck-flat-imgs/blue@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(flat.png) no-repeat; background: url(icheck-flat-imgs/flat.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat, .icheckbox_flat,
.iradio_flat { .iradio_flat {
background-image: url(flat@2x.png); background-image: url(icheck-flat-imgs/flat@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(green.png) no-repeat; background: url(icheck-flat-imgs/green.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-green, .icheckbox_flat-green,
.iradio_flat-green { .iradio_flat-green {
background-image: url(green@2x.png); background-image: url(icheck-flat-imgs/green@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(grey.png) no-repeat; background: url(icheck-flat-imgs/grey.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-grey, .icheckbox_flat-grey,
.iradio_flat-grey { .iradio_flat-grey {
background-image: url(grey@2x.png); background-image: url(icheck-flat-imgs/grey@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(orange.png) no-repeat; background: url(icheck-flat-imgs/orange.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-orange, .icheckbox_flat-orange,
.iradio_flat-orange { .iradio_flat-orange {
background-image: url(orange@2x.png); background-image: url(icheck-flat-imgs/orange@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(pink.png) no-repeat; background: url(icheck-flat-imgs/pink.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-pink, .icheckbox_flat-pink,
.iradio_flat-pink { .iradio_flat-pink {
background-image: url(pink@2x.png); background-image: url(icheck-flat-imgs/pink@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(purple.png) no-repeat; background: url(icheck-flat-imgs/purple.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-purple, .icheckbox_flat-purple,
.iradio_flat-purple { .iradio_flat-purple {
background-image: url(purple@2x.png); background-image: url(icheck-flat-imgs/purple@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(red.png) no-repeat; background: url(icheck-flat-imgs/red.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-red, .icheckbox_flat-red,
.iradio_flat-red { .iradio_flat-red {
background-image: url(red@2x.png); background-image: url(icheck-flat-imgs/red@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -9,7 +9,7 @@
padding: 0; padding: 0;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: url(yellow.png) no-repeat; background: url(icheck-flat-imgs/yellow.png) no-repeat;
border: none; border: none;
cursor: pointer; cursor: pointer;
} }
@ -49,7 +49,7 @@
only screen and (min-device-pixel-ratio: 1.5) { only screen and (min-device-pixel-ratio: 1.5) {
.icheckbox_flat-yellow, .icheckbox_flat-yellow,
.iradio_flat-yellow { .iradio_flat-yellow {
background-image: url(yellow@2x.png); background-image: url(icheck-flat-imgs/yellow@2x.png);
-webkit-background-size: 176px 22px; -webkit-background-size: 176px 22px;
background-size: 176px 22px; background-size: 176px 22px;
} }

View File

@ -59,6 +59,11 @@
}.bind( this ) ); }.bind( this ) );
$( window ).bind( "touchend", function( e ) { $( window ).bind( "touchend", function( e ) {
//COMUNIC FIX
if(!this.getCurrentRange)
return;
var isInside = ( editor.is( e.target ) || editor.has( e.target ).length > 0 ), var isInside = ( editor.is( e.target ) || editor.has( e.target ).length > 0 ),
currentRange = this.getCurrentRange(), currentRange = this.getCurrentRange(),
clear = currentRange && ( currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset ); clear = currentRange && ( currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset );

8
assets/3rdparty/identicon.js/LICENSE vendored Normal file
View File

@ -0,0 +1,8 @@
Copyright (c) 2018, Stewart Lord
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

73
assets/3rdparty/identicon.js/README.md vendored Normal file
View File

@ -0,0 +1,73 @@
identicon.js
============
![Screenshot](/screenshot.png)
[![CDNJS version](https://img.shields.io/cdnjs/v/identicon.js.svg)](https://cdnjs.com/libraries/identicon.js)
GitHub-style identicons as PNGs or SVGs in JS.
This little library will produce the same shape and (nearly) the same color as GitHub when given the same hash value. Supports PNG and SVG output formats. Note that GitHub uses an internal database identifier for the hash, so you can't simply md5 the username and get the same result. The creative visual design is borrowed from [Jason Long](http://twitter.com/jasonlong) of Git and GitHub fame.
Demo
----
[View Demo](https://rawgithub.com/stewartlord/identicon.js/master/demo.html)
Installation
-----
```
npm install identicon.js --save
```
[![NPM Stats](https://nodei.co/npm/identicon.js.png?downloads=true)](https://npmjs.org/package/identicon.js)
Options
----
* **hash** - A hexadecimal string of 15+ characters that will be used to generate the image.
* **options** - [Optional] An options object used to customize the generated image.
* **size** - The size in pixels of the height and width of the generated (square) image. Defaults to 64 pixels.
* **margin** - The decimal fraction of the size to use for margin. For example, use 0.2 for a 20% margin. Defaults to 0.08 for an 8% margin.
* **foreground** - The foreground color is automatically derived from the hash value. Use this option to override that behavior and provide a rgba value array instead (e.g. [255,0,0,255] for red).
* **background** - The background color expressed as an rgba value array to use for the image background. For example, use [255,0,0,255] for red. Defaults to an opaque light gray [240,240,240,255].
* **saturation** - The saturation of the derived foreground color as a value from 0-1. Defaults to 0.7.
* **brightness** - The brightness of the derived foreground color as a value from 0-1. Defaults to 0.5.
Usage
-----
##### Simple
Generate the Identicon by supplying a hash string and size.
```js
// create a base64 encoded PNG
var data = new Identicon('d3b07384d113edec49eaa6238ad5ff00', 420).toString();
// write to a data URI
document.write('<img width=420 height=420 src="data:image/png;base64,' + data + '">');
```
##### Advanced
To customize additional properties, generate the Identicon by supplying a hexadecimal string and an options object.
```js
// set up options
var hash = "c157a79031e1c40f85931829bc5fc552"; // 15+ hex chars
var options = {
foreground: [0, 0, 0, 255], // rgba black
background: [255, 255, 255, 255], // rgba white
margin: 0.2, // 20% margin
size: 420, // 420px square
format: 'svg' // use SVG instead of PNG
};
// create a base64 encoded SVG
var data = new Identicon(hash, options).toString();
// write to a data URI
document.write('<img width=420 height=420 src="data:image/svg+xml;base64,' + data + '">');
```
[PNG output requires PNGLib](http://www.xarg.org/download/pnglib.js)
Copyright 2018, [Stewart Lord](https://github.com/stewartlord)
Released under the [BSD license](http://www.opensource.org/licenses/bsd-license.php)

View File

@ -0,0 +1,205 @@
/**
* Identicon.js 2.3.2
* http://github.com/stewartlord/identicon.js
*
* PNGLib required for PNG output
* http://www.xarg.org/download/pnglib.js
*
* Copyright 2018, Stewart Lord
* Released under the BSD license
* http://www.opensource.org/licenses/bsd-license.php
*/
(function() {
var PNGlib;
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
PNGlib = require('./pnglib');
} else {
PNGlib = window.PNGlib;
}
var Identicon = function(hash, options){
if (typeof(hash) !== 'string' || hash.length < 15) {
throw 'A hash of at least 15 characters is required.';
}
this.defaults = {
background: [240, 240, 240, 255],
margin: 0.08,
size: 64,
saturation: 0.7,
brightness: 0.5,
format: 'png'
};
this.options = typeof(options) === 'object' ? options : this.defaults;
// backward compatibility with old constructor (hash, size, margin)
if (typeof(arguments[1]) === 'number') { this.options.size = arguments[1]; }
if (arguments[2]) { this.options.margin = arguments[2]; }
this.hash = hash
this.background = this.options.background || this.defaults.background;
this.size = this.options.size || this.defaults.size;
this.format = this.options.format || this.defaults.format;
this.margin = this.options.margin !== undefined ? this.options.margin : this.defaults.margin;
// foreground defaults to last 7 chars as hue at 70% saturation, 50% brightness
var hue = parseInt(this.hash.substr(-7), 16) / 0xfffffff;
var saturation = this.options.saturation || this.defaults.saturation;
var brightness = this.options.brightness || this.defaults.brightness;
this.foreground = this.options.foreground || this.hsl2rgb(hue, saturation, brightness);
};
Identicon.prototype = {
background: null,
foreground: null,
hash: null,
margin: null,
size: null,
format: null,
image: function(){
return this.isSvg()
? new Svg(this.size, this.foreground, this.background)
: new PNGlib(this.size, this.size, 256);
},
render: function(){
var image = this.image(),
size = this.size,
baseMargin = Math.floor(size * this.margin),
cell = Math.floor((size - (baseMargin * 2)) / 5),
margin = Math.floor((size - cell * 5) / 2),
bg = image.color.apply(image, this.background),
fg = image.color.apply(image, this.foreground);
// the first 15 characters of the hash control the pixels (even/odd)
// they are drawn down the middle first, then mirrored outwards
var i, color;
for (i = 0; i < 15; i++) {
color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg;
if (i < 5) {
this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image);
} else if (i < 10) {
this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
} else if (i < 15) {
this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
}
}
return image;
},
rectangle: function(x, y, w, h, color, image){
if (this.isSvg()) {
image.rectangles.push({x: x, y: y, w: w, h: h, color: color});
} else {
var i, j;
for (i = x; i < x + w; i++) {
for (j = y; j < y + h; j++) {
image.buffer[image.index(i, j)] = color;
}
}
}
},
// adapted from: https://gist.github.com/aemkei/1325937
hsl2rgb: function(h, s, b){
h *= 6;
s = [
b += s *= b < .5 ? b : 1 - b,
b - h % 1 * s * 2,
b -= s *= 2,
b,
b + h % 1 * s,
b + s
];
return[
s[ ~~h % 6 ] * 255, // red
s[ (h|16) % 6 ] * 255, // green
s[ (h|8) % 6 ] * 255 // blue
];
},
toString: function(raw){
// backward compatibility with old toString, default to base64
if (raw) {
return this.render().getDump();
} else {
return this.render().getBase64();
}
},
isSvg: function(){
return this.format.match(/svg/i)
}
};
var Svg = function(size, foreground, background){
this.size = size;
this.foreground = this.color.apply(this, foreground);
this.background = this.color.apply(this, background);
this.rectangles = [];
};
Svg.prototype = {
size: null,
foreground: null,
background: null,
rectangles: null,
color: function(r, g, b, a){
var values = [r, g, b].map(Math.round);
values.push((a >= 0) && (a <= 255) ? a/255 : 1);
return 'rgba(' + values.join(',') + ')';
},
getDump: function(){
var i,
xml,
rect,
fg = this.foreground,
bg = this.background,
stroke = this.size * 0.005;
xml = "<svg xmlns='http://www.w3.org/2000/svg'"
+ " width='" + this.size + "' height='" + this.size + "'"
+ " style='background-color:" + bg + ";'>"
+ "<g style='fill:" + fg + "; stroke:" + fg + "; stroke-width:" + stroke + ";'>";
for (i = 0; i < this.rectangles.length; i++) {
rect = this.rectangles[i];
if (rect.color == bg) continue;
xml += "<rect "
+ " x='" + rect.x + "'"
+ " y='" + rect.y + "'"
+ " width='" + rect.w + "'"
+ " height='" + rect.h + "'"
+ "/>";
}
xml += "</g></svg>"
return xml;
},
getBase64: function(){
if (btoa) {
return btoa(this.getDump());
} else if (Buffer) {
return new Buffer(this.getDump(), 'binary').toString('base64');
} else {
throw 'Cannot generate base64 output';
}
}
};
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = Identicon;
} else {
window.Identicon = Identicon;
}
})();

View File

@ -0,0 +1,19 @@
{
"name": "identicon.js",
"version": "2.3.2",
"description": "GitHub-style identicons as PNGs or SVGs in JS.",
"main": "identicon.js",
"repository": {
"type": "git",
"url": "https://github.com/stewartlord/identicon.js"
},
"keywords": [
"identicon"
],
"author": "stewardlord",
"license": "BSD",
"bugs": {
"url": "https://github.com/stewartlord/identicon.js/issues"
},
"homepage": "https://github.com/stewartlord/identicon.js"
}

214
assets/3rdparty/identicon.js/pnglib.js vendored Normal file
View File

@ -0,0 +1,214 @@
/**
* A handy class to calculate color values.
*
* @version 1.0
* @author Robert Eisele <robert@xarg.org>
* @copyright Copyright (c) 2010, Robert Eisele
* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*
*/
(function() {
// helper functions for that ctx
function write(buffer, offs) {
for (var i = 2; i < arguments.length; i++) {
for (var j = 0; j < arguments[i].length; j++) {
buffer[offs++] = arguments[i].charAt(j);
}
}
}
function byte2(w) {
return String.fromCharCode((w >> 8) & 255, w & 255);
}
function byte4(w) {
return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255);
}
function byte2lsb(w) {
return String.fromCharCode(w & 255, (w >> 8) & 255);
}
// modified from original source to support NPM
var PNGlib = function(width,height,depth) {
this.width = width;
this.height = height;
this.depth = depth;
// pixel data and row filter identifier size
this.pix_size = height * (width + 1);
// deflate header, pix_size, block headers, adler32 checksum
this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4;
// offsets and sizes of Png chunks
this.ihdr_offs = 0; // IHDR offset and size
this.ihdr_size = 4 + 4 + 13 + 4;
this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size
this.plte_size = 4 + 4 + 3 * depth + 4;
this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size
this.trns_size = 4 + 4 + depth + 4;
this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size
this.idat_size = 4 + 4 + this.data_size + 4;
this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size
this.iend_size = 4 + 4 + 4;
this.buffer_size = this.iend_offs + this.iend_size; // total PNG size
this.buffer = new Array();
this.palette = new Object();
this.pindex = 0;
var _crc32 = new Array();
// initialize buffer with zero bytes
for (var i = 0; i < this.buffer_size; i++) {
this.buffer[i] = "\x00";
}
// initialize non-zero elements
write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03");
write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE');
write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS');
write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT');
write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND');
// initialize deflate header
var header = ((8 + (7 << 4)) << 8) | (3 << 6);
header+= 31 - (header % 31);
write(this.buffer, this.idat_offs + 8, byte2(header));
// initialize deflate block headers
for (var i = 0; (i << 16) - 1 < this.pix_size; i++) {
var size, bits;
if (i + 0xffff < this.pix_size) {
size = 0xffff;
bits = "\x00";
} else {
size = this.pix_size - (i << 16) - i;
bits = "\x01";
}
write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size));
}
/* Create crc32 lookup table */
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
if (c & 1) {
c = -306674912 ^ ((c >> 1) & 0x7fffffff);
} else {
c = (c >> 1) & 0x7fffffff;
}
}
_crc32[i] = c;
}
// compute the index into a png for a given pixel
this.index = function(x,y) {
var i = y * (this.width + 1) + x + 1;
var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i;
return j;
}
// convert a color and build up the palette
this.color = function(red, green, blue, alpha) {
alpha = alpha >= 0 ? alpha : 255;
var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue;
if (typeof this.palette[color] == "undefined") {
if (this.pindex == this.depth) return "\x00";
var ndx = this.plte_offs + 8 + 3 * this.pindex;
this.buffer[ndx + 0] = String.fromCharCode(red);
this.buffer[ndx + 1] = String.fromCharCode(green);
this.buffer[ndx + 2] = String.fromCharCode(blue);
this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha);
this.palette[color] = String.fromCharCode(this.pindex++);
}
return this.palette[color];
}
// output a PNG string, Base64 encoded
this.getBase64 = function() {
var s = this.getDump();
var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var c1, c2, c3, e1, e2, e3, e4;
var l = s.length;
var i = 0;
var r = "";
do {
c1 = s.charCodeAt(i);
e1 = c1 >> 2;
c2 = s.charCodeAt(i+1);
e2 = ((c1 & 3) << 4) | (c2 >> 4);
c3 = s.charCodeAt(i+2);
if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); }
if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; }
r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4);
} while ((i+= 3) < l);
return r;
}
// output a PNG string
this.getDump = function() {
// compute adler32 of output pixels + row filter bytes
var BASE = 65521; /* largest prime smaller than 65536 */
var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
var s1 = 1;
var s2 = 0;
var n = NMAX;
for (var y = 0; y < this.height; y++) {
for (var x = -1; x < this.width; x++) {
s1+= this.buffer[this.index(x, y)].charCodeAt(0);
s2+= s1;
if ((n-= 1) == 0) {
s1%= BASE;
s2%= BASE;
n = NMAX;
}
}
}
s1%= BASE;
s2%= BASE;
write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1));
// compute crc32 of the PNG chunks
function crc32(png, offs, size) {
var crc = -1;
for (var i = 4; i < size-4; i += 1) {
crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
}
write(png, offs+size-4, byte4(crc ^ -1));
}
crc32(this.buffer, this.ihdr_offs, this.ihdr_size);
crc32(this.buffer, this.plte_offs, this.plte_size);
crc32(this.buffer, this.trns_offs, this.trns_size);
crc32(this.buffer, this.idat_offs, this.idat_size);
crc32(this.buffer, this.iend_offs, this.iend_size);
// convert PNG to string
return "\x89PNG\r\n\x1a\n"+this.buffer.join('');
}
}
// modified from original source to support NPM
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = PNGlib;
} else {
window.PNGlib = PNGlib;
}
})();

View File

@ -0,0 +1,121 @@
/*!
JSZipUtils - A collection of cross-browser utilities to go along with JSZip.
<http://stuk.github.io/jszip-utils>
(c) 2014 Stuart Knightley, David Duponchel
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown.
*/
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.JSZipUtils=e():"undefined"!=typeof global?global.JSZipUtils=e():"undefined"!=typeof self&&(self.JSZipUtils=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
var JSZipUtils = {};
// just use the responseText with xhr1, response with xhr2.
// The transformation doesn't throw away high-order byte (with responseText)
// because JSZip handles that case. If not used with JSZip, you may need to
// do it, see https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
JSZipUtils._getBinaryFromXHR = function (xhr) {
// for xhr.responseText, the 0xFF mask is applied by JSZip
return xhr.response || xhr.responseText;
};
// taken from jQuery
function createStandardXHR() {
try {
return new window.XMLHttpRequest();
} catch( e ) {}
}
function createActiveXHR() {
try {
return new window.ActiveXObject("Microsoft.XMLHTTP");
} catch( e ) {}
}
// Create the request object
var createXHR = window.ActiveXObject ?
/* Microsoft failed to properly
* implement the XMLHttpRequest in IE7 (can't request local files),
* so we use the ActiveXObject when it is available
* Additionally XMLHttpRequest can be disabled in IE7/IE8 so
* we need a fallback.
*/
function() {
return createStandardXHR() || createActiveXHR();
} :
// For all other browsers, use the standard XMLHttpRequest object
createStandardXHR;
JSZipUtils.getBinaryContent = function(path, callback) {
/*
* Here is the tricky part : getting the data.
* In firefox/chrome/opera/... setting the mimeType to 'text/plain; charset=x-user-defined'
* is enough, the result is in the standard xhr.responseText.
* cf https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Receiving_binary_data_in_older_browsers
* In IE <= 9, we must use (the IE only) attribute responseBody
* (for binary data, its content is different from responseText).
* In IE 10, the 'charset=x-user-defined' trick doesn't work, only the
* responseType will work :
* http://msdn.microsoft.com/en-us/library/ie/hh673569%28v=vs.85%29.aspx#Binary_Object_upload_and_download
*
* I'd like to use jQuery to avoid this XHR madness, but it doesn't support
* the responseType attribute : http://bugs.jquery.com/ticket/11461
*/
try {
var xhr = createXHR();
xhr.open('GET', path, true);
// recent browsers
if ("responseType" in xhr) {
xhr.responseType = "arraybuffer";
}
// older browser
if(xhr.overrideMimeType) {
xhr.overrideMimeType("text/plain; charset=x-user-defined");
}
//Allow big files download
xhr.setRequestHeader("Range", "bytes=0-9999999999999999999999999999999");
xhr.onreadystatechange = function(evt) {
var file, err;
// use `xhr` and not `this`... thanks IE
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 0) {
file = null;
err = null;
try {
file = JSZipUtils._getBinaryFromXHR(xhr);
} catch(e) {
err = new Error(e);
}
callback(err, file);
} else {
callback(new Error("Ajax error for " + path + " : " + this.status + " " + this.statusText), null);
}
}
};
xhr.send();
} catch (e) {
callback(new Error(e), null);
}
};
// export
module.exports = JSZipUtils;
// enforcing Stuk's coding style
// vim: set shiftwidth=4 softtabstop=4:
},{}]},{},[1])
(1)
});
;

View File

@ -0,0 +1,10 @@
/*!
JSZipUtils - A collection of cross-browser utilities to go along with JSZip.
<http://stuk.github.io/jszip-utils>
(c) 2014 Stuart Knightley, David Duponchel
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown.
*/
!function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.JSZipUtils=a():"undefined"!=typeof global?global.JSZipUtils=a():"undefined"!=typeof self&&(self.JSZipUtils=a())}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b){"use strict";function c(){try{return new window.XMLHttpRequest}catch(a){}}function d(){try{return new window.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}}var e={};e._getBinaryFromXHR=function(a){return a.response||a.responseText};var f=window.ActiveXObject?function(){return c()||d()}:c;e.getBinaryContent=function(a,b){try{var c=f();c.open("GET",a,!0),"responseType"in c&&(c.responseType="arraybuffer"),c.overrideMimeType&&c.overrideMimeType("text/plain; charset=x-user-defined"),c.onreadystatechange=function(){var d,f;if(4===c.readyState)if(200===c.status||0===c.status){d=null,f=null;try{d=e._getBinaryFromXHR(c)}catch(g){f=new Error(g)}b(f,d)}else b(new Error("Ajax error for "+a+" : "+this.status+" "+this.statusText),null)},c.send()}catch(d){b(new Error(d),null)}},b.exports=e},{}]},{},[1])(1)});

11623
assets/3rdparty/jszip/jszip.js vendored Normal file

File diff suppressed because it is too large Load Diff

15
assets/3rdparty/jszip/jszip.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
/**
* Errors pages stylesheet
*
* @author Pierre HUBERT
*/
.error-page .error-content p {
word-wrap: break-word;
}

View File

@ -20,6 +20,29 @@ header .dropdown .fa-gear {
vertical-align: middle; vertical-align: middle;
} }
.main-header .alternate-latest-posts-button {
display: none;
}
@media (max-width: 360px) {
.main-header[forActiveUser="true"] .navbar-brand {
display: none;
}
.main-header .alternate-latest-posts-button {
display: block;
}
.nav > li > a {
padding: 10px 10px;
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
width: 100%;
left: 0px;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.skin-blue .main-header .navbar .dropdown-user-menu-action .dropdown-menu li a { .skin-blue .main-header .navbar .dropdown-user-menu-action .dropdown-menu li a {
color: #333 !important; color: #333 !important;

483
assets/css/dark_theme.css Normal file
View File

@ -0,0 +1,483 @@
/**
* ComunicWeb dark theme
*
* @author Pierre HUBERT
*/
/**
* General definitions
*/
:root {
--black: #1119;
--black2: #111111;
--black3: #000;
--black4: #686a6c;
--black5: #232426;
--black6: #0f0f0f;
--white: silver;
}
p, h1, h2, h3, h4, h5, h6 {
color: var(--white);
}
.content-wrapper {
background-color: var(--black3);
}
.link-black {
color: var(--white);
}
.table-hover > tbody > tr:hover {
background-color: var(--black3);
}
/**
* Buttons
*/
.btn {
color: var(--white);
}
.btn-primary,
.btn-success,
.btn-default,
.btn-danger,
.btn-info {
background-color: transparent;
}
.btn-default:active, .btn-default:focus {
background-color: var(--white);
}
/**
* Forms
*/
.form-control {
background-color: var(--black);
color: var(--white);
border-color: var(--black5);
}
.form-control:focus {
background-color: var(--black5);
border-color: var(--black4);
}
.form-group label {
color: var(--white);
}
.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control {
background-color: var(--black6);
}
/**
* Boxes
*/
.box {
background-color: var(--black5);
color: var(--white);
}
.box-footer {
background-color: var(--black5);
}
/**
* Modals
*/
.modal-header,
.modal-footer {
background-color: var(--black5);
}
.modal-body {
background-color: var(--black6);
}
.modal-primary .modal-header,
.modal-primary .modal-footer {
background-color: var(--black5) !important;;
}
.modal-primary .modal-body {
background-color: var(--black6) !important;
}
.modal-danger .modal-header,
.modal-danger .modal-footer {
background-color: #a22819 !important;
}
.modal-danger .modal-body {
background-color: #872f24 !important;
}
/**
* Dropdown menus
*/
.dropdown-menu {
background-color: var(--black5) !important;
}
.dropdown-menu > li > a {
color: var(--white) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--black3) !important;
}
.dropdown {
color: var(--white);
}
/**
* Navbar
*/
.navbar {
background-color: var(--black6) !important;;
}
.navbar-nav > .notifications-menu > .dropdown-menu > li.header,
.navbar-nav > .messages-menu > .dropdown-menu > li.header,
.navbar-nav > .tasks-menu > .dropdown-menu > li.header {
background-color: var(--black6);
color: var(--white);
}
.navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a,
.navbar-nav > .messages-menu > .dropdown-menu > li.footer > a,
.navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a {
background-color: var(--black6) !important;
color: var(--white) !important;
}
.main-header #navbar-search-input.form-control:focus,
.main-header #navbar-search-input.form-control:active {
background-color: var(--black3);
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > h4 {
color: var(--white);
}
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a:hover,
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:hover,
.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a:hover {
background-color: var(--black6);
}
.skin-blue .main-header .navbar .dropdown-user-menu-action .dropdown-menu li a {
color: var(--white) !important;;
}
.skin-blue .main-header .navbar .nav > li > a:hover,
.skin-blue .main-header .navbar .nav > li > a:active,
.skin-blue .main-header .navbar .nav > li > a:focus,
.skin-blue .main-header .navbar .nav .open > a,
.skin-blue .main-header .navbar .nav .open > a:hover,
.skin-blue .main-header .navbar .nav .open > a:focus,
.skin-blue .main-header .navbar .nav > .active > a {
background-color: var(--black4);
}
/**
* Footer
*/
.main-footer {
background-color: var(--black3);
}
.main-footer span {
color: var(--white);
}
/**
* Friends list
*/
@media screen and (min-width: 1200px) {
#friendsList {
background-color: var(--black6);
}
#friendsList:hover {
background-color: var(--black5);
}
}
@media screen and (max-width: 1200px) and (min-width: 513px) {
#friendsList {
background-color: var(--black5);
}
}
#friendsList h4 {
color: var(--white);
}
#friendsList li:hover {
background-color: var(--black4);
}
/**
* Emoji picker
*/
.wdt-emoji-popup {
background-color: var(--black6);
border: 1px var(--black6) solid;
border-radius: 2;
}
#wdt-emoji-menu-header {
background-color: var(--black5);
}
#wdt-emoji-footer {
background-color: var(--black5);
}
.wdt-emoji-popup h3 {
background-color: var(--black4);
color: var(--white);
}
#wdt-emoji-search {
background-color: var(--black5);
color: var(--white);
}
#wdt-emoji-search:focus {
background-color: var(--black4);
color: var(--white);
}
.wdt-emoji:hover {
background-color: var(--black4) !important;;
}
/**
* Conversations
*/
.open-conversation-button {
background-color: var(--black5);
}
.nav-stacked > li > a {
color: var(--white);
}
.conversations-list-box .last-activity {
color: var(--white);
}
.nav > li > a:hover,
.nav > li > a:active,
.nav > li > a:focus {
color: var(--black5);
}
.select2-dropdown {
background-color: var(--black5);
color: var(--white);
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: var(--black4);
color: var(--white);
}
#conversationsElem .create-message-form .btn.btn-add-emoji:hover,
#conversationsElem .create-message-form .btn.btn-add-image:hover {
color: var(--white);
}
/**
* Home page
*/
#homeLandingScreen {
background-image: none !important;
background-color: var(--black5);
}
#homeLandingScreen #homeMessage {
background-color: var(--black);
}
/**
* Login page
*/
.login-page, .register-page {
background-color: var(--black2);
}
.login-box-body {
background-color: var(--black3);
}
.login-logo b, #loginForm label {
color: var(--white) !important;
}
/**
* Create account page
*/
.create-account-form label {
color: var(--white);
}
/**
* User information
*/
.list-group-item {
background-color: transparent;
}
/**
* Posts
*/
.attachment-block {
background-color: var(--black6);
}
.post-comments {
background-color: var(--black);
border-radius: 2px;
}
.post {
color: var(--white);
}
.post-image {
filter: brightness(80%);
transition: filter 1s;
}
.post-image:hover {
filter: brightness(100%);
}
.box-comments .username,
.box-comments .comment-content {
color: var(--white);
}
.comment-creation-form .comment-image-select a,
.comment-creation-form .comment-emoji-select a {
color: var(--white);
}
/**
* Posts creation form
*/
.post-form-choice span {
color: var(--black4);
}
.post-form .post-form-choice input:checked ~ span {
color: var(--white);
}
.datepicker.dropdown-menu {
background-color: var(--black6);
color: var(--white);
}
.select2-container--default .select2-selection--multiple {
background-color: transparent;
}
.post-form .post-visiblity-container span {
color: var(--black4);
}
.post-form .post-visiblity-container input:checked ~ span {
color: var(--white);
}
/**
* Posts edit modal
*/
.editor {
background-color: var(--black5);
color: var(--white);
}
/**
* Search page
*/
.nav > li > a:hover,
.nav > li > a:active,
.nav > li > a:focus {
background-color: var(--black4);
}
/**
* Conversation page
*/
.conversations-page-container a {
background-color: var(--black6) !important;;
}
.conversations-page-container a {
color: var(--white) !important;
}
.conversations-page-container a:hover {
background-color: var(--black5) !important;;
}
.conversations-page-container a:active {
background-color: var(--black6) !important;;
}
.conversations-page-container a.selected {
background-color: var(--black5) !important;;
}
.conversations-page-container .box-conversation .input-group-btn .btn {
background-color: var(--black5);
}
.conversations-page-container .box-conversation .input-group-btn .btn:hover {
background-color: var(--black4);
color: var(--black6);
}
.conversations-page-container .box-conversation .input-group-btn .btn:focus,
.conversations-page-container .box-conversation .input-group-btn .btn:active {
color: var(--white);
}
.big-box-conversation .direct-chat-text .a,
.big-box-conversation .direct-chat-text a {
background-color: transparent !important;
color: inherit !important;
}

View File

@ -0,0 +1,29 @@
/**
* Conversation pane stylesheet
*
* @author Pierre HUBERT
*/
.big-box-conversation .direct-chat-text {
display: inline-block;
margin-left: 10px;
max-width: 150px;
word-wrap: break-word;
}
.big-box-conversation .right .direct-chat-text {
float: right;
margin-right: 10px;
}
.big-box-conversation .direct-chat-text .message-img {
max-width: 100%;
max-height: 200px;
display: block;
margin: auto;
}
.big-box-conversation .direct-chat-text .a,
.big-box-conversation .direct-chat-text a {
color: white;
}

View File

@ -0,0 +1,17 @@
/**
* Conversations list pane
*
* @author Pierre HUBERT
*/
.conversations-page-container .conversations-list-box {
max-height: 100%;
}
.conversations-page-container .conversations-list-box ul {
max-height: 100%;
}
.conversations-page-container a.selected {
background: #f7f7f7;
}

View File

@ -0,0 +1,17 @@
/**
* Conversations page main script file
*
* @author Pierre HUBERT
*/
.conversations-page-container {
max-width: 1000px;
margin: auto;
margin-top: 10px;
}
.conversations-page-container .conv-list-loading-msg {
text-align: center;
padding: 10px;
}

View File

@ -0,0 +1,25 @@
/**
* Create groups page main stylesheet
*
* @author Pierre HUBERT
*/
.create-group-page {
max-width: 450px;
margin: auto;
padding-top: 60px;
}
.create-group-page form {
border: 1px #162935 solid;
padding: 15px;
}
.create-group-page form h3 {
text-align: center;
margin-top: 0px;
}
.create-group-page .submit-button-container {
text-align: center;
}

View File

@ -0,0 +1,22 @@
/**
* Access forbidden to page groups stylesheet
*
* @author Pierre HUBERT
*/
.group-forbidden-page-container {
max-width: 300px;
margin: auto;
margin-top: 15px;
text-align: center;
}
.group-forbidden-page-container .group-logo {
width: 130px;
margin: auto;
display: block;
}
.group-forbidden-page-container .group-name {
margin-bottom: 15px;
}

View File

@ -0,0 +1,14 @@
/**
* Group page main stylesheet
*
* @author Pierre HUBERT
*/
.group-page {
padding-top: 10px;
}
.group-page .col-md-6 {
margin: auto;
float: none;
}

View File

@ -0,0 +1,54 @@
/**
* Groups main page stylesheet
*
* @author Pierre HUBERT
*/
.groups-main-page {
max-width: 450px;
margin: auto;
text-align: center;
padding-top: 50px;
}
.groups-main-page .group-item {
text-align: justify;
margin-top: 10px;
}
.groups-main-page .group-item .group-icon {
max-width: 50px;
margin-right: 10px;
}
.groups-main-page .group-item div {
display: inline-block;
}
.groups-main-page .group-item .group-name {
width: 180px;
vertical-align: middle;
}
.groups-main-page .group-item .buttons-area {
width: 20px;
}
.groups-main-page .group-item .buttons-area .fa {
color: #b5bbc8;
}
.groups-main-page .group-item .buttons-area .fa:active {
color: black;
}
@media screen and (max-width: 450px){
.groups-main-page {
max-width: 317px;
}
.groups-main-page .group-item .group-name {
width: 120px;
}
}

View File

@ -0,0 +1,55 @@
/**
* Groups members page stylesheet
*
* @author Pierre HUBERT
*/
.group-members-page {
float: none;
margin: auto;
margin-top: 40px;
}
.group-members-page .backward-link {
margin-bottom: 10px;
}
.group-members-page .title {
text-align: center;
}
.group-members-page .member {
margin-bottom: 10px;
}
.group-members-page .member div {
display: inline-block;
}
.group-members-page .member .user-image {
border-radius: 50%;
max-width: 50px;
height: auto;
}
.group-members-page .member .member-name {
margin-left: 5px;
font-weight: bold;
width: 150px;
}
.group-members-page .member .delete-link {
cursor: pointer;
}
.group-members-page .member .delete-link:active {
color: #3c8dbc;
}
.group-members-page .member .delete-link {
width: 20px;
}
.group-members-page .member .btn-membership-level {
width: 130px;;
}

View File

@ -0,0 +1,38 @@
/**
* Group settings stylesheet
*
* @author Pierre HUBERT
*/
.group-settings-container {
float: none;
margin: auto;
margin-top: 40px;
}
.group-settings-container .title {
text-align: center;
margin-bottom: 15px;
}
.group-settings-container .separated-block {
margin-top: 25px;
margin-bottom: 30px;
display: block;
}
.group-settings-container .submit-button-container {
text-align: center;
}
.group-settings-container .groupLogoSettingsContainer {
margin-top: 100px;
text-align: center;
}
.group-settings-container .group-logo-settings-img {
width: 150px;
margin: auto;
margin-bottom: 10px;
display: block;
}

View File

@ -0,0 +1,37 @@
/**
* Groups header
*
* @author Pierre HUBERT
*/
.group-header .group-icon {
max-height: 150px;
}
.group-header .group-name {
font-size: 200%;
}
.group-header .group-tag {
color: #6f737b;
}
.group-header .col-info .fa,
.group-header .col-metadata .fa {
margin-right: 10px;
width: 10px;
}
.group-header .group-col-icon {
text-align: center;
}
.group-header .group-col-icon span {
display: block;
}
.group-header .col-md-4 {
display: table;
margin: auto;
}

View File

@ -0,0 +1,9 @@
/**
* Membership block stylesheet
*
* @author Pierre HUBERT
*/
.reject-group-invitation-link {
color: #f56954;
}

View File

@ -0,0 +1,9 @@
/**
* Groups posts section stylesheet
*
* @author Pierre HUBERT
*/
.box-group-posts .second-user-area {
display: none;
}

View File

@ -21,4 +21,6 @@
.latest-posts-row .col-md-5 { .latest-posts-row .col-md-5 {
margin: auto; margin: auto;
float: none; float: none;
width: 100%;
max-width: 800px;
} }

View File

@ -0,0 +1,18 @@
/**
* Mail admin to reset password
*
* @author Pierre HUBERT
*/
.reset-password-by-mail-step .title {
text-align: center;
margin-top: 0px;
}
.reset-password-by-mail-step p {
text-align: justify;
}
.reset-password-by-mail-step .btn-mail {
width: 100%;
}

View File

@ -0,0 +1,11 @@
/**
* Password forgotten page main stylesheet
*
* @author Pierre HUBERT
*/
.box-password-forgotten {
max-width: 350px;
margin: auto;
margin-top: 20px;
}

View File

@ -0,0 +1,14 @@
/**
* Password forgotten prompt option section stylesheet
*
* @author Pierre HUBERT
*/
.password-reset-prompt-option-section p {
text-align: justify;
}
.password-reset-prompt-option-section .btn {
width: 100%;
margin-top: 10px;
}

View File

@ -0,0 +1,15 @@
/**
* Reset password main script
*
* @author Pierre HUBERT
*/
.reset-password-box {
max-width: 300px;
margin: auto;
margin-top: 30px;
}
.reset-password-box .btn {
display: block;
}

View File

@ -0,0 +1,24 @@
/**
* Search page main stylesheet
*
* @author Pierre HUBERT
*/
.searchPage {
max-width: 500px;
margin: auto;
margin-top: 30px;
}
.searchPage .results-container {
margin-top: 30px;
}
.searchPage .results-container li a img {
margin: auto 10px auto auto;
max-width: 40px;
height: 40px;
border-radius: 50%;
visibility: visible;
}

View File

@ -0,0 +1,5 @@
/**
* Settings language section
*
* @author Pierre HUBERT
*/

View File

@ -0,0 +1,13 @@
/**
* Settings privacy settings section
*
* @author Pierre HUBERT
*/
.box-export-account-data-settings .btn {
float: right;
}
.box-delete-account-settings .btn {
float: right;
}

View File

@ -0,0 +1,9 @@
/**
* User posts stylesheet
*
* @author Pierre HUBERT
*/
.box-user-posts .second-user-area {
display: none;
}

View File

@ -27,7 +27,7 @@ ComunicWeb.common.date = {
//Check there was less than one minute //Check there was less than one minute
if(difference == 0) if(difference == 0)
return seconds + "s"; return lang("dates_s", [seconds]);
//Calculate minutes //Calculate minutes
@ -36,7 +36,7 @@ ComunicWeb.common.date = {
//Check there was less than one hour //Check there was less than one hour
if(difference == 0) if(difference == 0)
return minutes + "min"; return lang("dates_min", [minutes]);
//Calculate hours //Calculate hours
@ -45,7 +45,7 @@ ComunicWeb.common.date = {
//Check there was less than a day //Check there was less than a day
if(difference == 0) if(difference == 0)
return hours + "h"; return lang("dates_h", [hours]);
//Calculate days //Calculate days
@ -55,9 +55,9 @@ ComunicWeb.common.date = {
//Check there was less than a month //Check there was less than a month
if(difference == 0){ if(difference == 0){
if(days == 1) if(days == 1)
return "1 day"; return lang("dates_one_day");
else else
return days + " days"; return lang("dates_days", [days]);
} }
@ -68,19 +68,19 @@ ComunicWeb.common.date = {
//Check there was less than a year //Check there was less than a year
if(difference == 0){ if(difference == 0){
if(months == 1) if(months == 1)
return "1 month"; return lang("dates_one_month");
else else
return months + " months"; return lang("dates_months", [months]);
} }
//Calculate years //Calculate years
var years = difference; var years = difference;
if(years == 1){ if(years == 1){
return "1 year"; return lang("dates_one_year");
} }
else { else {
return years + " years"; return lang("dates_years", [years]);
} }
}, },

View File

@ -74,7 +74,7 @@ var ComunicWeb = {
/** /**
* Return current language * Return current language
*/ */
getCurrentLanguage: function(){}, current: function(){},
/** /**
* Initializate languages * Initializate languages
@ -191,6 +191,11 @@ var ComunicWeb = {
*/ */
refresh_current_page: function(){}, refresh_current_page: function(){},
/**
* Safely trigger URL update
*/
update_uri: function(title, uri){},
/** /**
* Inform of page location update * Inform of page location update
*/ */
@ -392,6 +397,8 @@ var ComunicWeb = {
* Logout user * Logout user
*/ */
logoutUser: function(afterLogout){}, logoutUser: function(afterLogout){},
//TODO : implement
}, },
/** /**
@ -468,6 +475,26 @@ var ComunicWeb = {
//TODO : implement //TODO : implement
}, },
/**
* Account export
*/
export: {
/**
* UI controller
*/
ui: {
//TODO : implement
},
/**
* Worker
*/
worker: {
//TODO: implement
},
},
}, },
/** /**
@ -582,8 +609,36 @@ var ComunicWeb = {
/** /**
* Search form component * Search form component
*/ */
searchForm: { search: {
//TODO : implement
/**
* Search form
*/
form: {
//TODO : implement
},
/**
* Search interface
*/
interface: {
//TODO : implement
},
/**
* Search results UI
*/
ui: {
//TODO : implement
},
/**
* Search utilities
*/
utils: {
//TODO : implement
},
}, },
/** /**
@ -598,6 +653,13 @@ var ComunicWeb = {
//TODO : implement //TODO : implement
}, },
/**
* Settings helper
*/
helper: {
//TODO : implement
},
}, },
/** /**
@ -961,8 +1023,48 @@ var ComunicWeb = {
//TODO : implement //TODO : implement
} }
} },
/**
* Groups component
*/
groups: {
/**
* API interface
*/
interface: {
//TODO : implement
},
/**
* Groups utilities
*/
utils: {
//TODO : implement
},
/**
* Groups information
*/
info: {
//TODO : implement
},
},
/**
* Virtual directories component
*/
virtualDirectory: {
/**
* API interface
*/
interface: {
//TODO : implement
},
},
}, },
/** /**
@ -1100,6 +1202,137 @@ var ComunicWeb = {
}, },
/**
* Conversations page
*/
conversations: {
/**
* Main script
*/
main: {
//TODO : implement
},
/**
* Conversations list pane
*/
listPane: {
//TODO : implement
},
/**
* Conversation pane
*/
conversation: {
//TODO : implement
},
/**
* Conversation page utilities
*/
utils: {
//TODO : implement
},
},
/**
* Groups page
*/
groups: {
/**
* Groups page main script
*/
main: {
//TODO : implement
},
/**
* Groups pages
*/
pages: {
/**
* Main page
*/
main: {
//TODO : implement
},
/**
* Create a group page
*/
create: {
//TODO : implement
},
/**
* Main group page
*/
group: {
//TODO : implement
},
/**
* Settings page
*/
settings: {
//TODO : implement
},
/**
* Group members page
*/
members: {
//TODO : implement
},
/**
* Access to group forbidden page
*/
forbidden: {
//TODO : implemement
},
},
/**
* Groups sections
*/
sections: {
/**
* Header section
*/
header: {
//TODO : implement
},
/**
* Membership block
*/
membershipBlock: {
//TODO : implement
},
/**
* Posts sections
*/
posts: {
//TODO : implement
},
/**
* Follow block
*/
followBlock: {
//TODO : implement
},
},
},
/** /**
* User settings page * User settings page
*/ */
@ -1138,6 +1371,13 @@ var ComunicWeb = {
//TODO : implement //TODO : implement
}, },
/**
* Language section
*/
language: {
//TODO : implement
},
/** /**
* Security section * Security section
*/ */
@ -1158,6 +1398,13 @@ var ComunicWeb = {
accountImage: { accountImage: {
//TODO : implement //TODO : implement
}, },
/**
* Privacy section
*/
privacy: {
//TODO : implement
},
}, },
}, },
@ -1199,6 +1446,62 @@ var ComunicWeb = {
//TODO : implement //TODO : implement
}, },
/**
* Password forgotten page
*/
passwordForgotten: {
/**
* Main script
*/
main: {
//TODO : implement
},
/**
* Ask user email step
*/
promptEmail: {
//TODO : implement
},
/**
* Ask user reset option
*/
promptOption: {
//TODO : implement
},
/**
* Option : send a mail to the admin
*/
mailAdmin: {
//TODO : implement
},
/**
* Option : prompt security questions
*/
promptSecurityQuestions: {
//TODO : implement
},
},
/**
* Password reset page
*/
resetPassword: {
/**
* Main script
*/
main: {
//TODO : implement
},
},
/** /**
* Logout controller * Logout controller
*/ */
@ -1209,5 +1512,32 @@ var ComunicWeb = {
openLogoutPage: function(additionnalData, targetElement){}, openLogoutPage: function(additionnalData, targetElement){},
}, },
/**
* Virtual directory pages
*/
virtualDirectory: {
/**
* Main page
*/
page: {
//TODO : implement
},
},
/**
* Search page
*/
search: {
/**
* Main page
*/
main: {
//TODO : implement
},
},
}, },
}; };

View File

@ -6,6 +6,22 @@
ComunicWeb.common.langs = { ComunicWeb.common.langs = {
/**
* Languages list
*/
list: {
en: {
name: "English",
local_name: "English"
},
fr: {
name: "French",
local_name: "Français"
}
},
/** /**
* Local storage lang item name * Local storage lang item name
*/ */
@ -20,18 +36,26 @@ ComunicWeb.common.langs = {
* Default langage (updated on init) * Default langage (updated on init)
*/ */
__defaultLang: "en", __defaultLang: "en",
/** /**
* Get current language * Get current language
* *
* @return {String} The id of the current language * @return {String} The id of the current language
*/ */
getCurrentLanguage: function(){ current: function(){
//Check if a language has been set in local storage //Check if a language has been set in local storage
if(localStorage.getItem(this._lang_storage_field_name) != null) if(localStorage.getItem(this._lang_storage_field_name) != null)
return localStorage.getItem(this._lang_storage_field_name); return localStorage.getItem(this._lang_storage_field_name);
//Get user language
var language = navigator.language || navigator.userLanguage;
if(language){
return language.split("-")[0];
}
//Else return default language //Else return default language
return ComunicWeb.__config.defaultLanguage; return ComunicWeb.__config.defaultLanguage;
}, },
@ -61,7 +85,7 @@ ComunicWeb.common.langs = {
ComunicWeb.debug.logMessage("Get and install languages..."); ComunicWeb.debug.logMessage("Get and install languages...");
//Get languages to install //Get languages to install
this.__currentLang = this.getCurrentLanguage(); this.__currentLang = this.current();
this.__defaultLang = ComunicWeb.__config.defaultLanguage; this.__defaultLang = ComunicWeb.__config.defaultLanguage;
//Everything is OK //Everything is OK
@ -76,6 +100,10 @@ ComunicWeb.common.langs = {
* @return {String} The string ready to show * @return {String} The string ready to show
*/ */
getTranslatedText: function(stringName, stringParams){ getTranslatedText: function(stringName, stringParams){
if(!this[this.__currentLang])
return "Invalid language!";
//Try to get string //Try to get string
if(this[this.__currentLang][stringName]) if(this[this.__currentLang][stringName])
var string = this[this.__currentLang][stringName]; var string = this[this.__currentLang][stringName];

View File

@ -47,7 +47,7 @@ ComunicWeb.common.messages.createCalloutElem = function(calloutTitle, calloutMes
*/ */
ComunicWeb.common.messages.createLoadingCallout = function(target){ ComunicWeb.common.messages.createLoadingCallout = function(target){
var elem = this.createCalloutElem("Loading", "Please wait while this page is loading...", "info"); var elem = this.createCalloutElem(lang("messages_loading_layout_title"), lang("messages_loading_layout_message"), "info");
if(target) if(target)
target.appendChild(elem); target.appendChild(elem);
@ -62,7 +62,7 @@ ComunicWeb.common.messages.createLoadingCallout = function(target){
* *
* @param {object} info Information about the callout to create * @param {object} info Information about the callout to create
* @argument {string} type The type of modal * @argument {string} type The type of modal
* @param {string} title The title of the modal * @argument {string} title The title of the modal
* @return {object} Information about the created dialog * @return {object} Information about the created dialog
*/ */
ComunicWeb.common.messages.createDialogSkeleton = function(info){ ComunicWeb.common.messages.createDialogSkeleton = function(info){
@ -138,7 +138,7 @@ ComunicWeb.common.messages.createDialogSkeleton = function(info){
appendTo: data.modalFooter, appendTo: data.modalFooter,
type: "button", type: "button",
class: "btn btn-default", class: "btn btn-default",
innerHTML: "Cancel" innerHTML: lang("messages_dialog_cancel")
}); });
return data; return data;
@ -197,7 +197,7 @@ ComunicWeb.common.messages.confirm = function(message, callback){
appendTo: modalHeader, appendTo: modalHeader,
type: "h4", type: "h4",
class: "modal-title", class: "modal-title",
innerHTML: "Confirm the operation" innerHTML: lang("messages_dialog_confirm_title")
}); });
//Modal body //Modal body
@ -219,7 +219,7 @@ ComunicWeb.common.messages.confirm = function(message, callback){
appendTo: modalFooter, appendTo: modalFooter,
type: "button", type: "button",
class: "btn btn-default pull-left", class: "btn btn-default pull-left",
innerHTML: "Cancel" innerHTML: lang("messages_dialog_confirm_cancel")
}); });
cancelButton.setAttribute("data-confirm", "false"); cancelButton.setAttribute("data-confirm", "false");
@ -227,7 +227,7 @@ ComunicWeb.common.messages.confirm = function(message, callback){
appendTo: modalFooter, appendTo: modalFooter,
type: "button", type: "button",
class: "btn btn-danger", class: "btn btn-danger",
innerHTML: "Confirm" innerHTML: lang("messages_dialog_confirm_confirm")
}); });
confirmButton.setAttribute("data-confirm", "true"); confirmButton.setAttribute("data-confirm", "true");
@ -341,7 +341,7 @@ ComunicWeb.common.messages.inputString = function(title, message, defaultValue,
appendTo: modalFooter, appendTo: modalFooter,
type: "button", type: "button",
class: "btn btn-default pull-left", class: "btn btn-default pull-left",
innerHTML: "Cancel" innerHTML: lang("messages_dialog_input_string_cancel")
}); });
cancelButton.setAttribute("data-confirm", "false"); cancelButton.setAttribute("data-confirm", "false");
@ -349,7 +349,7 @@ ComunicWeb.common.messages.inputString = function(title, message, defaultValue,
appendTo: modalFooter, appendTo: modalFooter,
type: "button", type: "button",
class: "btn btn-primary", class: "btn btn-primary",
innerHTML: "Submit" innerHTML: lang("messages_dialog_input_string_submit")
}); });
submitButton.setAttribute("data-confirm", "true"); submitButton.setAttribute("data-confirm", "true");

View File

@ -157,20 +157,23 @@ ComunicWeb.common.page = {
firstPartURI = "home"; firstPartURI = "home";
} }
//Save the first part of the URI as an argument
additionnalData.rootDirectory = firstPartURI;
//Check if there is also subfolders //Check if there is also subfolders
if(firstPartURI.indexOf("/") != -1){ if(firstPartURI.indexOf("/") != -1){
//Save the list of subfolders //Save the list of subfolders
var subfoldersURIarray = firstPartURI.split("/"); var subfoldersURIarray = firstPartURI.split("/");
subfoldersURIarray.shift(); subfoldersURIarray.shift();
subfoldersURI = subfoldersURIarray.join("/"); var subfoldersURI = subfoldersURIarray.join("/");
//Remove them to find the right page //Remove them to find the right page
firstPartURI = firstPartURI.split("/")[0]; firstPartURI = firstPartURI.split("/")[0];
} else { } else {
//No subfolder was specified //No subfolder was specified
subfoldersURI = false; var subfoldersURI = false;
} }
//Check if specied page exists //Check if specied page exists
@ -179,18 +182,25 @@ ComunicWeb.common.page = {
} }
//Else we include the 404 not found page //Else we include the 404 not found page
else{ else {
var pageInfos = ComunicWeb.pagesList.notFound;
//Check if no subfolder was specified
if(subfoldersURI)
var pageInfos = ComunicWeb.pagesList.notFound;
//Find & open dynamically the appropriate page
else {
var pageInfos = ComunicWeb.pagesList.virtual_directory;
}
} }
//Change page title //Change page title
document.title = pageInfos.pageTitle; document.title = pageInfos.pageTitle;
//Change page URL //Change page URL, if required
ComunicWeb.common.url.changeURI(document.title, pageURI); if(additionnalData.no_url_update ? !additionnalData.no_url_update : true)
this.update_uri(document.title, pageURI);
//Save new url
this._current_url = window.location.href.toString();
//Get the main container of the page //Get the main container of the page
var mainContainerElem = byId("wrapper"); var mainContainerElem = byId("wrapper");
@ -200,6 +210,12 @@ ComunicWeb.common.page = {
var mainContainerElem = this.emptyPage(true); var mainContainerElem = this.emptyPage(true);
} }
//Check if the page requires user login
if(pageInfos.needLogin){
if(!signed_in())
openPage("login");
}
//We check if the page is a full screen page or not //We check if the page is a full screen page or not
if(pageInfos.disableMenus){ if(pageInfos.disableMenus){
//We force the screen to be cleaned //We force the screen to be cleaned
@ -274,7 +290,22 @@ ComunicWeb.common.page = {
var currentPage = ComunicWeb.common.url.getCurrentWebsiteURL(); var currentPage = ComunicWeb.common.url.getCurrentWebsiteURL();
//Open a page //Open a page
this.openPage(currentPage); this.openPage(currentPage, {no_url_update: true});
},
/**
* Safely trigger URL update
*
* @param {String} title The new title of the page
* @param {String} uri The new URL
*/
update_uri: function(title, uri){
//Trigger URL update
ComunicWeb.common.url.changeURI(title, uri);
//Save new url
this._current_url = window.location.href.toString();
}, },
/** /**
@ -339,10 +370,25 @@ ComunicWeb.common.page = {
} }
} }
//Apply required translations
while(templateContent.includes("[[")){
//Get the full template inclusion
var source = templateContent.match(/\[\[.*\]\]/i)[0];
//Determine lang key
var key = source.replace("[[", "").replace("]]", "");
var translation = lang(key);
//Apply lang
while(templateContent.includes(source))
templateContent = templateContent.replace(source, translation);
}
//Apply template source //Apply template source
targetElem.innerHTML = templateContent; targetElem.innerHTML = templateContent;
//Make a link live //Make links live
var aElems = targetElem.getElementsByTagName("a"); var aElems = targetElem.getElementsByTagName("a");
for(num in aElems){ for(num in aElems){

View File

@ -38,9 +38,36 @@ function openPage(pageURI, additionnalData){
* @return {Boolean} True for a success * @return {Boolean} True for a success
*/ */
function openUserPage(user){ function openUserPage(user){
if(user.virtualDirectory == "")
openUserPageFromID(user.userID);
else
openPage(user.virtualDirectory);
}
/**
* Open a user page quickly from its user ID
*
* @param {String} user The ID of the user or its directory
* @return {Boolean} True for a success
*/
function openUserPageFromID(user){
return openPage("user/" + user); return openPage("user/" + user);
} }
/**
* Open a group age
*
* @param {Object} info Information about the target group
*/
function openGroupPage(info){
if(info.virtual_directory != "null")
openPage(info.virtual_directory);
else
openPage("groups/" + info.id);
}
/** /**
* Check if user is signed in or not * Check if user is signed in or not
* *
@ -123,4 +150,24 @@ function openConversation(id){
*/ */
function notify(message, type, duration, title){ function notify(message, type, duration, title){
ComunicWeb.common.notificationSystem.showNotification(message, type, duration, title) ComunicWeb.common.notificationSystem.showNotification(message, type, duration, title)
}
/**
* Get information about a single group
*
* @param {Number} id The ID of the group to fetch
* @param {Function} callback
*/
function getInfoGroup(id, callback){
ComunicWeb.components.groups.info.getInfo(id, callback);
}
/**
* Get information about multiple groups
*
* @param {Array} IDs The IDs of the groups to get information about
* @param {Function} callback Callback to call once we have information about the group
*/
function getInfoMultipleGroups(IDs, callback){
ComunicWeb.components.groups.info.getInfoMultiple(IDs, callback);
} }

View File

@ -19,6 +19,11 @@ ComunicWeb.common.system = {
//Start init //Start init
ComunicWeb.debug.logMessage("Start initialization..."); ComunicWeb.debug.logMessage("Start initialization...");
//Enable error reporting
window.addEventListener("error", function(e){
ComunicWeb.common.error.syntaxtError(e.error);
});
//Disable tooltips //Disable tooltips
$(function () { $(function () {
$(document.body).tooltip("disable"); $(document.body).tooltip("disable");
@ -52,11 +57,8 @@ ComunicWeb.common.system = {
* Open a page * Open a page
*/ */
if(!openPage){ if(!openPage){
//Get current page URI //Refresh current page
var currentPage = ComunicWeb.common.url.getCurrentWebsiteURL(); ComunicWeb.common.page.refresh_current_page();
//Open a page
ComunicWeb.common.page.openPage(currentPage);
} }
else else
//Open specified page //Open specified page

View File

@ -39,6 +39,7 @@ function createElem(nodeType, appendTo){
* @info {String} value The value of the new element * @info {String} value The value of the new element
* @info {String} placeholder The placeholder of the new element * @info {String} placeholder The placeholder of the new element
* @info {String} innerHTML Specify the html content of the newly created element * @info {String} innerHTML Specify the html content of the newly created element
* @info {String} innerLang Specify the key of the lang to use to fill the element
* @info {boolean} disabled Set whether the field should be disabled or not (input only) * @info {boolean} disabled Set whether the field should be disabled or not (input only)
* @return {HTMLElement} The newly created element * @return {HTMLElement} The newly created element
*/ */
@ -101,6 +102,9 @@ function createElem2(infos){
//Specify node content //Specify node content
if(infos.innerHTML) if(infos.innerHTML)
newElem.innerHTML = infos.innerHTML; newElem.innerHTML = infos.innerHTML;
if(infos.innerLang)
newElem.innerHTML = lang(infos.innerLang);
//Set field state //Set field state
if(infos.disabled) if(infos.disabled)
@ -460,6 +464,21 @@ function add_space(target){
} }
/**
* Create and append a new paragraph
*
* @param {HTMLElement} target The target for the new paragraph
* @param {String} content The new content for the paragraph
* @returns {HTMLElement} Generated element
*/
function add_p(target, content){
return createElem2({
appendTo: target,
type: "p",
innerHTML: content
});
}
/** /**
* Get the current absolute position bottom of the screen * Get the current absolute position bottom of the screen
* *
@ -507,4 +526,70 @@ window.location.changed = function(e){};
} }
})(jQuery); })(jQuery);
/**
* Generate a new random string
*
* @param {number} length The length of the string ot generate
* @return {string} Generated string
*/
function random_string(length){
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst0123456789";
for(var i = 0; i < length; i++)
text += possible.charAt(Math.random() * possible.length);
return text;
}
/**
* Generate and return a new random identity image
*
* @return {string} Base64 - encoded new identity image
*/
function generateIdentImage() {
var hash = random_string(32);
var color = Math.random() * 240;
var color2 = Math.random() * 240;
var color3 = Math.random() * 240;
var options = {
foreground: [color, color2, color3, 255],
size: 130,
margin: 0.2,
format: 'png'
};
return new Identicon(hash, options).toString();
}
/**
* Turn a data URI into blob
*
* This function is based on Stoive answer at StackOverFlow question #4998908
*
* @param {string} dataURI The URI to process
* @return {Blob} generated blob
*/
function dataURItoBlob(dataURI){
//convert base64 / URLEncoded data component to raw binary data held in a string
var byteString;
if(dataURI.split(",")[0].indexOf("base64") >= 0)
byteString = atob(dataURI.split(",")[1]);
else
byteString = unescape(dataURI.split(",")[1]);
//Separate the out the mime component
var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0];
//Write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for(var i = 0; i < byteString.length; i++)
ia[i] = byteString.charCodeAt(i);
return new Blob([ia], {type: mimeString});
}

View File

@ -0,0 +1,106 @@
/**
* Account export UI controller
*
* @author Pierre HUBERT
*/
ComunicWeb.components.account.export.ui = {
/**
* Account export modal information
*/
_exportModal: {},
/**
* Request account export
*
* @param {String} password The password of the user
*/
requestExport: function(password){
//Reset modal information
this._exportModal = {};
//Create the modal
this._exportModal = ComunicWeb.common.messages.createDialogSkeleton({
title: "Exporting data"
});
var modal = this._exportModal.modal;
$(modal).modal("show");
//Add message
createElem2({
appendTo: this._exportModal.modalBody,
type: "p",
innerHTML: "Please do not close this window while we create your archive..."
});
//Add progress bar
var progressContainer = createElem2({
appendTo: this._exportModal.modalBody,
type: "div",
class: "progress progress-xs progress-striped active"
});
this._exportModal.progress = createElem2({
appendTo: progressContainer,
type: "div",
class: "progress-bar progress-bar-success"
});
this.updateProgress(1);
//Add message
this._exportModal.messageContainer = createElem2({
appendTo: this._exportModal.modalBody,
type: "p",
innerHTML: "Starting..."
});
//Create close modal function
var closeModal = function(){
$(modal).modal('hide');
emptyElem(modal);
modal.remove();
}
this._exportModal.close = closeModal;
this._exportModal.closeModal.onclick = closeModal;
this._exportModal.cancelButton.onclick = closeModal;
//Start the worker
ComunicWeb.components.account.export.worker.start(password);
},
/**
* Update the progress of the creation of the archive
*
* @param {Number} progress The new percentage to apply
*/
updateProgress: function(progress){
this._exportModal.progress.style.width = progress + "%";
},
/**
* Update the message shown on the screen
*
* @param {String} message The new message
*/
updateMessage: function(message){
this._exportModal.messageContainer.innerHTML = message;
},
/**
* Display an error that prevent the success of the operation
*
* @param {String} message The message of the error
*/
exportFatalError: function(message){
//Get modal body
var modalBody = this._exportModal.modalBody;
emptyElem(modalBody);
//Display the error message
var msg = ComunicWeb.common.messages.createCalloutElem("Could not export your data", "An error occurred while trying to export your data: <i>" + message + "</i>", "danger");
modalBody.appendChild(msg);
}
}

View File

@ -0,0 +1,231 @@
/**
* Account data export worker
*
* @author Pierre HUBERT
*/
ComunicWeb.components.account.export.worker = {
/**
* Start account export
*
* @param {String} password The password of the user
*/
start: function(password){
//Get all user text data from the interface
ComunicWeb.components.account.interface.exportData(password, function(result){
//Check for errors
if(result.error){
return ComunicWeb.components.account.export.ui.exportFatalError("Could not get text data! Please check your password...");
}
//Update progress
ComunicWeb.components.account.export.ui.updateMessage("Got text data");
ComunicWeb.components.account.export.ui.updateProgress(10);
//Parse data
ComunicWeb.components.account.export.worker.parse(result);
});
},
/**
* Parse account text data into ZIP file
*
* @param {Object} data Text data about the account
*/
parse: function(data){
//Get UI shorcut
var ui = ComunicWeb.components.account.export.ui;
var Promise = window.Promise;
if (!Promise) {
Promise = JSZip.external.Promise;
}
/**
* Fetch the content and return the associated promise.
* @param {String} url the url of the content to fetch.
* @return {Promise} the promise containing the data.
*/
function urlToPromise(url) {
return new Promise(function(resolve, reject) {
JSZipUtils.getBinaryContent(url, function (err, data) {
if(err) {
reject(err);
} else {
resolve(data);
}
});
});
}
/**
* Transform an URL into a path in the archive
*
* @param {String} url The URL to transform
* @return {String} Generated file path
*/
function urlToPath(url) {
var path = url.replace("://", "/");
return "files/" + path;
}
//Determine the list of files to download
var files_list = this._generate_files_list(data);
//Create zip file
var zip = new JSZip();
//Add raw json file
zip.file("source.json", JSON.stringify(data));
//Add the files to download
files_list.forEach(function(url){
var path = urlToPath(url);
zip.file(path, urlToPromise(url), {binary:true});
});
//Generated zip archive
zip.generateAsync({type:"blob"}, function updateCallback(metadata) {
var msg = "progression : " + metadata.percent.toFixed(2) + " %";
if(metadata.currentFile) {
msg += ", current file = " + metadata.currentFile;
}
ui.updateMessage(msg);
ui.updateProgress(metadata.percent.toFixed(2));
})
//Trigger download
.then(function callback(blob) {
//Download file
saveAs(blob, "accountData.zip");
//Update progress
ui.updateProgress(100);
ui.updateMessage("Done !");
}, function (e) {
//In case of error
ComunicWeb.components.account.export.ui.exportFatalError(e);
//Update progress
ui.updateProgress(100);
ui.updateMessage("Error !");
});
},
/**
* Determine the list of files to download
*
* @param {Object} data Dataset to parse
* @return {Array} Generated dataset
*/
_generate_files_list: function(data){
var files = [];
/**
* Parse user information to find potential files to download
*
* @param {Object} info Information about the comment to parse
*/
var parseUserInfo = function(info){
if(info.accountImage != null)
if(!files.includes(info.accountImage))
files.push(info.accountImage);
}
/**
* Parse a comment to find potential files to download
*
* @param {Object} info Information about the comment to parse
*/
var parseComment = function(info){
if(info.img_url != null)
if(!files.includes(info.img_url))
files.push(info.img_url);
}
/**
* Parse a post to find potential files to download
*
* @param {Object} info Information about the post to parse
*/
var parsePost = function(post){
if(post.kind != "youtube"){
if(post.file_path_url != null){
if(!files.includes(post.file_path_url))
files.push(post.file_path_url);
}
}
//Parse comments
post.comments.forEach(parseComment);
}
/**
* Parse a movie to find potential files to download
*
* @param {Object} info Information about the movie to parse
*/
var parseMovie = function(info){
if(info.url != null)
if(!files.includes(info.url))
files.push(info.url);
}
/**
* Parse a conversation message to find potential files to download
*
* @param {Object} info Information about the movie to parse
*/
var parseConversationMessage = function(info){
if(info.image_path != null)
if(!files.includes(info.image_path))
files.push(info.image_path);
}
//Main account information
files.push(data.advanced_info.accountImage);
files.push(data.advanced_info.backgroundImage);
//Posts
data.posts.forEach(parsePost);
//Comments
data.comments.forEach(parseComment);
//Movie
data.movies.forEach(parseMovie);
//Conversation message
//* All from users
data.all_conversation_messages.forEach(parseConversationMessage);
//* All from conversations
for (var num in data.conversations_messages) {
if (data.conversations_messages.hasOwnProperty(num)) {
var conversation = data.conversations_messages[num];
conversation.forEach(parseConversationMessage);
}
}
//Related user information
for (var key in data.users_info) {
if (data.users_info.hasOwnProperty(key)) {
parseUserInfo(data.users_info[key]);
}
}
return files;
}
}

View File

@ -31,4 +31,135 @@ ComunicWeb.components.account.interface = {
}, },
/**
* Check whether an email address is linked to an account or not
*
* @param {String} email The email address to check
* @param {function} callback
*/
existsMail: function(email, callback){
var apiURI = "account/exists_email";
var params = {
email: email
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Check whether the security questions have been set for an account
* with an email address or not
*
* @param {String} email The email address of the account
* @param {function} callback
*/
checkSecurityQuestionsExistence: function(email, callback){
var apiURI = "account/has_security_questions";
var params = {
email: email
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Given an email address, returns the security questions of a user
*
* @param {String} email The email address of the account
* @param {function} callback
*/
getSecurityQuestions: function(email, callback){
var apiURI = "account/get_security_questions";
var params = {
email: email
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get passowrd reset token using security answer
*
* @param {String} email The email address of the account
* @param {Array} answers The answers to the security questions
* @param {Function} callback
*/
resetPasswordWithSecurityAnswers: function(email, answers, callback){
//Prepare answers
answersText = "";
answers.forEach(function(answer){
if(answersText != "")
answersText += "&";
answersText += encodeURIComponent(answer);
});
//Perform the request
var apiURI = "account/check_security_answers";
var params = {
email: email,
answers: answersText
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Check the validity of a reset password token
*
* @param {String} token The token to check
* @param {Function} callback
*/
checkPasswordResetToken: function(token, callback){
var apiURI = "account/check_password_reset_token";
var params = {
token: token
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, false, callback);
},
/**
* Reset user password
*
* @param {String} token The token to check
* @param {String} passwd The new password for the user
* @param {Function} callback
*/
resetUserPassword: function(token, passwd, callback){
var apiURI = "account/reset_user_passwd";
var params = {
token: token,
password: passwd
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, false, callback);
},
/**
* Request the export of all the data of the user
*
* @param {String} password The password of the user
* @param {function} callback
*/
exportData: function(password, callback){
var apiURI = "account/export_data";
var params = {
password: password
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Request the deletion of the account
*
* @param {string} password The password of the account
* @param {function} callback
*/
deleteAccount: function(password, callback){
var apiURI = "account/delete";
var params = {
password: password
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
} }

View File

@ -52,6 +52,18 @@ ComunicWeb.components.bottom.main = {
langLink.onclick = function(){ langLink.onclick = function(){
ComunicWeb.components.langPicker.show(); ComunicWeb.components.langPicker.show();
}; };
add_space(leftElements);
add_space(leftElements);
//Add about link
var aboutLink = createElem2({
appendTo: leftElements,
type: "a",
innerHTML: "<i class='fa fa-question-circle'></i> About",
href: ComunicWeb.__config.aboutWebsiteURL
});
aboutLink.setAttribute("target", "_blank");
} }
} }

View File

@ -25,7 +25,7 @@ ComunicWeb.components.comments.actions = {
//Check for errors //Check for errors
if(result.error){ if(result.error){
ComunicWeb.common.notificationSystem.showNotification("Couldn't get informations about a comment !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("comments_actions_err_get_info_single"), "danger");
return; return;
} }

View File

@ -26,7 +26,7 @@ ComunicWeb.components.comments.editor = {
//Check for error //Check for error
if(result.error){ if(result.error){
ComunicWeb.common.notificationSystem.showNotification("An error occured while trying to update comment content !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("comments_editor_err_update"), "danger");
return; return;
} }
@ -39,8 +39,8 @@ ComunicWeb.components.comments.editor = {
//Prompt the user to enter the new content of the comment //Prompt the user to enter the new content of the comment
ComunicWeb.common.messages.inputString( ComunicWeb.common.messages.inputString(
"Edit comment content", lang("comments_editor_title"),
"Please specify the new content of the comment: ", lang("comments_editor_notice"),
infos.content, infos.content,
inputCallback inputCallback
); );

View File

@ -46,9 +46,10 @@ ComunicWeb.components.comments.form = {
type: "input", type: "input",
elemType: "text", elemType: "text",
class: "form-control", class: "form-control",
placeholder: "New comment...", placeholder: lang("comments_form_input_placeholder"),
name: "content" name: "content"
}); });
newCommentText.maxLength = 255;
//Add button group //Add button group
@ -105,7 +106,7 @@ ComunicWeb.components.comments.form = {
appendTo: buttonsGroup, appendTo: buttonsGroup,
type: "button", type: "button",
class: "btn btn-default btn-flat", class: "btn btn-default btn-flat",
innerHTML: "Send" innerHTML: lang("comments_form_send")
}); });
//Catch form when submitted //Catch form when submitted
@ -116,15 +117,22 @@ ComunicWeb.components.comments.form = {
//Check the comment //Check the comment
if(!hasImage && newCommentText.value < 5){ if(!hasImage && newCommentText.value < 5){
ComunicWeb.common.notificationSystem.showNotification("Please type a valid comment! (at least 5 characters)", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("comments_form_err_invalid_comment"), "danger");
return false; return false;
} }
//Lock send button //Lock send button
sendButton.disabled = true; sendButton.disabled = true;
//Try to create the comment //Prepare the request
var formData = new FormData(commentForm); var formData = new FormData();
formData.append("content", newCommentText.value);
//Check for image
if(imageFile.files.length > 0)
formData.append("image", imageFile.files[0], imageFile.files[0].name);
//Send the request
ComunicWeb.components.comments.interface.create(postID, formData, function(result){ ComunicWeb.components.comments.interface.create(postID, formData, function(result){
//Unlock send button //Unlock send button
@ -132,7 +140,7 @@ ComunicWeb.components.comments.form = {
//Check for errors //Check for errors
if(result.error){ if(result.error){
ComunicWeb.common.notificationSystem.showNotification("Couldn't create comment! (check its content)", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("comments_form_err_create_comment"), "danger");
return; return;
} }

View File

@ -23,7 +23,7 @@ ComunicWeb.components.comments.ui = {
//Check for errors //Check for errors
if(result.error){ if(result.error){
ComunicWeb.common.notificationSystem.showNotification("Couldn't informations about some users to display their comments !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("comments_ui_err_get_users_info"), "danger");
return; return;
} }
@ -75,7 +75,7 @@ ComunicWeb.components.comments.ui = {
//Check for errors //Check for errors
if(result.error){ if(result.error){
ComunicWeb.common.notificationSystem.showNotification("Couldn't get informations about a user!", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("comments_ui_err_get_user_info"), "danger");
return; return;
} }
@ -147,7 +147,7 @@ ComunicWeb.components.comments.ui = {
createElem2({ createElem2({
appendTo: rightContainer, appendTo: rightContainer,
type: "span", type: "span",
innerHTML: ComunicWeb.common.date.timeDiffToStr(infos.time_sent) + " ago" innerHTML: lang("dates_ago", [ComunicWeb.common.date.timeDiffToStr(infos.time_sent)])
}); });
//Offer the user the possibility to delete and update the comment if he is allowed to do so //Offer the user the possibility to delete and update the comment if he is allowed to do so
@ -191,7 +191,7 @@ ComunicWeb.components.comments.ui = {
//Make delete button lives //Make delete button lives
deleteCommentLink.onclick = function(){ deleteCommentLink.onclick = function(){
ComunicWeb.common.messages.confirm("Are you sure do you want to delete this comment ? This operation is unrecoverable!", function(response){ ComunicWeb.common.messages.confirm(lang("comments_ui_confirm_delete"), function(response){
//Check if user cancelled the operation //Check if user cancelled the operation
if(!response) if(!response)
@ -207,7 +207,7 @@ ComunicWeb.components.comments.ui = {
//Check for errors //Check for errors
if(response.error){ if(response.error){
ComunicWeb.common.notificationSystem.showNotification("Could not delete comment!", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("comments_ui_err_delete_comment"), "danger");
return; return;
} }

View File

@ -385,6 +385,10 @@ ComunicWeb.components.conversations.chatWindows = {
*/ */
changeName: function(newName, infos){ changeName: function(newName, infos){
//Reduce new name
if(newName.length > 18)
newName = newName.slice(0, 17) + "...";
//Empty name field //Empty name field
emptyElem(infos.boxTitle); emptyElem(infos.boxTitle);

View File

@ -180,7 +180,7 @@ ComunicWeb.components.conversations.interface = {
if(result.error){ if(result.error){
//Log error //Log error
ComunicWeb.debug.logMessage("Couldn't get informations about the conversation number "+conversationID+" !") ComunicWeb.debug.logMessage("Couldn't get informations about the conversation number "+conversationID+" !");
//Perform next action now //Perform next action now
nextStep(result); nextStep(result);
@ -343,6 +343,26 @@ ComunicWeb.components.conversations.interface = {
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback); ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
}, },
/**
* Get the the lastest messages of a single conversation
*
* @param {Number} convID Target conversation ID
* @param {Number} lastMessageID The ID of the last known message
* @param {function} callback
*/
refreshSingleConversation: function(convID, lastMessageID, callback){
//Perform a request on the API
var apiURI = "conversations/refresh_single";
var params = {
conversationID: convID,
last_message_id: lastMessageID
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/** /**
* Empty conversations cache * Empty conversations cache
* *

View File

@ -51,7 +51,7 @@ ComunicWeb.components.conversations.unreadDropdown = {
appendTo: dropdownMenu, appendTo: dropdownMenu,
type: "li", type: "li",
class: "header", class: "header",
innerHTML: "Unread conversations" innerHTML: lang("conversations_dropdown_header")
}); });
//Add conversations list //Add conversations list
@ -107,7 +107,7 @@ ComunicWeb.components.conversations.unreadDropdown = {
//Check for errors //Check for errors
if(conversations.error){ if(conversations.error){
//Display an error //Display an error
ComunicWeb.common.notificationSystem.showNotification("Could not retrieve the list of unread conversations !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("conversations_dropdown_err_get_list"), "danger");
return; return;
} }
@ -129,7 +129,7 @@ ComunicWeb.components.conversations.unreadDropdown = {
//Check for errors //Check for errors
if(usersInfos.error){ if(usersInfos.error){
//Display an error //Display an error
ComunicWeb.common.notificationSystem.showNotification("Could not get informations about some users !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("conversations_dropdown_err_get_user_info"), "danger");
return; return;
} }
@ -243,7 +243,7 @@ ComunicWeb.components.conversations.unreadDropdown = {
appendTo: target, appendTo: target,
type: "p", type: "p",
class: "no-unread-conversation-msg", class: "no-unread-conversation-msg",
innerHTML: "You do not have any unread messages in the conversations you are following..." innerHTML: lang("conversations_dropdown_no_unread_notice")
}); });
} }

View File

@ -125,7 +125,7 @@ ComunicWeb.components.friends.bar = {
noFriendMessage.style.color = "#3c8dbc"; noFriendMessage.style.color = "#3c8dbc";
noFriendMessage.style.fontSize = "150%"; noFriendMessage.style.fontSize = "150%";
noFriendMessage.style.textAlign = "justify"; noFriendMessage.style.textAlign = "justify";
noFriendMessage.innerHTML = "You have no friends yet! We can't display anything here for you for now... :(" noFriendMessage.innerHTML = lang("friends_bar_no_friends_notice")
} }
//Enable slimscroll //Enable slimscroll
@ -159,7 +159,7 @@ ComunicWeb.components.friends.bar = {
//Make the link lives //Make the link lives
friendLink.onclick = function(){ friendLink.onclick = function(){
openUserPage(userIDorPath(userInfos)); openUserPage(userInfos);
//For the responsive mode //For the responsive mode
ComunicWeb.components.friends.bar.toggleShowHide(); ComunicWeb.components.friends.bar.toggleShowHide();
@ -276,9 +276,9 @@ ComunicWeb.components.friends.bar = {
//Change statusRow style //Change statusRow style
if(accepted) if(accepted)
statusRow.innerHTML = "Accepted"; statusRow.innerHTML = lang("friends_bar_accepted");
else else
statusRow.innerHTML = "Refused"; statusRow.innerHTML = lang("friends_bar_rejected");
//Perform an API request //Perform an API request
ComunicWeb.components.friends.list.respondRequest(friendID, accepted); ComunicWeb.components.friends.list.respondRequest(friendID, accepted);

View File

@ -213,7 +213,7 @@ ComunicWeb.components.friends.listModal = {
userLink.onclick = function(){ userLink.onclick = function(){
//Open user page //Open user page
openUserPage(userIDorPath(users["user-" + id])); openUserPage(users["user-" + id]);
//Close all modals //Close all modals
$(".modal").modal("hide"); $(".modal").modal("hide");

View File

@ -55,7 +55,7 @@ ComunicWeb.components.friends.ui = {
userLink.onclick = function(){ userLink.onclick = function(){
//Open user page //Open user page
openUserPage(userIDorPath(user)); openUserPage(user);
//Close all modals //Close all modals
$(".modal").modal("hide"); $(".modal").modal("hide");

View File

@ -0,0 +1,113 @@
/**
* Groups information management
*
* @author Pierre HUBERT
*/
ComunicWeb.components.groups.info = {
/**
* Group information cache
*/
_cache: {},
/**
* Get information about a single group
*
* @param {Number} id The ID of the target group
* @param {Function} callback
*/
getInfo: function(id, callback){
//First, check if the group is cached or not
if(this._cache[id])
return callback(this._cache[id]);
//Then query the server, if required
ComunicWeb.components.groups.interface.getInfo(id, function(result){
//Check for errors
if(result.error)
return callback(result);
//Save group information
ComunicWeb.components.groups.info._cache[id] = result;
//Call callback
callback(result);
});
},
/**
* Get information about a multiple groups
*
* @param {Array} list The list of the IDs of the group to get information about
* @param {Function} callback
*/
getInfoMultiple: function(list, callback){
//First, check which group are unknown in the cache
var toFetch = Array();
list.forEach(function(id){
if(!ComunicWeb.components.groups.info._cache[id])
toFetch.push(id);
});
if(toFetch.length == 0){
this.getInfoMultiplePreCallback(list, callback);
return;
}
//Perform the request over the server
ComunicWeb.components.groups.interface.getInfoMultiple(toFetch, function(result){
//Check for errors
if(result.error)
return notify("Could not get information about the groups!", "danger");
//Process the list of groups
for(i in result){
//Save group information in the cache
ComunicWeb.components.groups.info._cache[result[i].id] = result[i];
}
//Call callback
ComunicWeb.components.groups.info.getInfoMultiplePreCallback(list, callback);
});
},
/**
* Get multiple information pre-callback
*
* @param {Array} list The list of the IDs of teh group to get information about
* @param {Function} callback
*/
getInfoMultiplePreCallback: function(list, callback){
var groupInfo = {};
list.forEach(function(id){
groupInfo[id] = ComunicWeb.components.groups.info._cache[id];
});
//Call callback
callback(groupInfo);
},
/**
* Clear cache
*/
clearCache: function(){
this._cache = {};
}
};
//Register cache cleaner
ComunicWeb.common.cacheManager.registerCacheCleaner("ComunicWeb.components.groups.info.clearCache");

View File

@ -0,0 +1,341 @@
/**
* Groups API interface
*
* @author Pierre HUBERT
*/
ComunicWeb.components.groups.interface = {
/**
* Create a group
*
* @param {String} name The name of the group to create
* @param {Function} callback
*/
create: function(name, callback){
//Perform a request over the API
var apiURI = "groups/create";
var params = {
name: name
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get the list of groups of the user
*
* @param {Function} callback
*/
getListUser: function(callback){
//Perform a request over the API
var apiURI = "groups/get_my_list";
var params = {};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Remove a user membership
*
* @param {Number} groupID The ID of the target group
* @param {Function} callback
*/
removeMembership: function(groupID, callback){
//Perform the request over the API
var apiURI = "groups/remove_membership";
var params = {
id: groupID
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get information about a group
*
* @param {Number} id The ID of the target group
* @param {Function} callback Callback
*/
getInfo: function(id, callback){
//Perform the request over the API
var apiURI = "groups/get_info";
var params = {
id: id
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get information about multiple groups
*
* @param {Array} list The IDs of the groups to get
* @param {Function} callback
*/
getInfoMultiple: function(list, callback){
//Perform the request over the API
var apiURI = "groups/get_multiple_info";
var params = {
list: list
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get advanced information about a group
*
* @param {Number} id The ID of the target group
* @param {Function} callback Callback
*/
getAdvancedInfo: function(id, callback){
//Perform the request over the API
var apiURI = "groups/get_advanced_info";
var params = {
id: id
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get the settings of a group
*
* @param {Number} id The ID of the target group
* @param {Function} callback
*/
getSettings: function(id, callback){
//Perform the request over the API
var apiURI = "groups/get_settings";
var params = {
id: id
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Set (update) the settings of a group
*
* @param {Number} id The ID of the target group
* @param {Object} settings The new settings to apply to
* the group
* @param {Function} callback
*/
setSettings: function(id, settings, callback){
//Perform the request over the API
var apiURI = "groups/set_settings";
settings.id = id;
ComunicWeb.common.api.makeAPIrequest(apiURI, settings, true, callback);
},
/**
* Check the availability of a virtual directory for a group
*
* @param {String} directory The directory to check
* @param {Number} groupID The ID of the group to check
* @param {Function} callback
*/
checkVirtualDirectory: function(directory, groupID, callback){
//Perform the request over the API
var apiURI = "groups/checkVirtualDirectory";
var params = {
groupID: groupID,
directory: directory
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Upload a new group logo
*
* @param {Number} id The ID of the target group
* @param {FormData} data The form data that contains the
* new logo (parameter name : logo)
* @param {Function} callback
*/
uploadLogo: function(id, data, callback){
//Perform the request over the API
var apiURI = "groups/upload_logo";
data.append("id", id);
ComunicWeb.common.api.makeFormDatarequest(apiURI, data, true, callback);
},
/**
* Delete user logo
*
* @param {Number} id The ID of the target group
* @param {Function} callback
*/
deleteLogo: function(id, callback){
//Perform the request over the API
var apiURI = "groups/delete_logo";
var params = {
id: id
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Respond to a group invitation
*
* @param {Number} id The ID of the target group
* @param {Boolean} accept Specify whether the invitation was
* accepted or not
* @param {Function} callback
*/
respondInvitation: function(id, accept, callback) {
//Perform the request over the API
var apiURI = "groups/respond_invitation";
var params = {
id: id,
accept: accept
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Cancel a membership request
*
* @param {Number} id The ID of the target group
* @param {Function} callback
*/
cancelRequest: function(id, callback){
//Perform the request over the API
var apiURI = "groups/cancel_request";
var params = {
id: id
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Send a request to join a group
*
* @param {Number} id The ID of the target group
* @param {Function} callback
*/
sendRequest: function(id, callback){
//Perform the request over the API
var apiURI = "groups/send_request";
var params = {
id: id
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get the members of a group
*
* @param {Number} id The ID of the target group
* @param {Function} callback
*/
getMembers: function(id, callback){
//Perform the request over the API
var apiURI = "groups/get_members";
var params = {
id: id
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Get information about a single user membership
*
* @param {Number} userID The ID of the target user
* @param {Number} groupID The ID of the target group
* @param {Function} callback The result
*/
getMembership: function(userID, groupID, callback){
//Perform the request over the API
var apiURI = "groups/get_membership";
var params = {
groupID: groupID,
userID: userID
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Remove (delete) a member from the group
*
* @param {Number} groupID The ID of the target group
* @param {Number} userID The ID of the target user
* @param {Function} callback
*/
deleteMember: function(groupID, userID, callback){
//Perform the request over the API
var apiURI = "groups/delete_member";
var params = {
groupID: groupID,
userID: userID
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Respond to a membership request
*
* @param {Number} groupID The ID of the target group
* @param {Number} userID The ID of the target user
* @param {Boolean} accept Specify whether the request is accepted or not
*/
respondRequest: function(groupID, userID, accept, callback){
//Perform the request over the API
var apiURI = "groups/respond_request";
var params = {
groupID: groupID,
userID: userID,
accept: accept
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Cancel a membership invitation
*
* @param {Number} groupID The ID of the target group
* @param {Number} userID The ID of the target user
* @param {Function} callback
*/
cancelInvitation: function(groupID, userID, callback){
//Perform the request over the API
var apiURI = "groups/cancel_invitation";
var params = {
groupID: groupID,
userID: userID
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Update the membership of a user
*
* @param {Number} groupID The ID of the target group
* @param {Number} userID The ID of the target user
* @param {String} level The new membership level for the user
* @param {Function} callback
*/
updateMembership: function(groupID, userID, level, callback){
//Perform the request over the API
var apiURI = "groups/update_membership_level";
var params = {
groupID: groupID,
userID: userID,
level: level
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
},
/**
* Set whether a user is following a group or not
*
* @param {Number} groupID The ID of the target group
* @param {Boolean} follow
* @param {Function} callback
*/
setFollowing: function(groupID, follow, callback){
//Perform the request over the API
var apiURI = "groups/set_following";
var params = {
groupID: groupID,
follow: follow
};
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
}
};

View File

@ -0,0 +1,61 @@
/**
* Groups utilities
*
* @author Pierre HUBERT
*/
ComunicWeb.components.groups.utils = {
/**
* Extract users ids from members list
*
* @param {Array} list The list of members to process
* @return {Array} The list of the IDs of the members of group
*/
getMembersIDs: function(list){
var IDs = [];
//Process the list of IDs
list.forEach(function(member){
IDs.push(member.user_id);
});
return IDs;
},
/**
* Check whether a user is a member (or more) of a group or not
*
* @param {Object} info Information about the target group
* @return {boolean} TRUE if the user is a member of the group
* FALSE else
*/
isGroupMember: function(info){
return info.membership == "member"
|| info.membership == "moderator"
|| info.membership == "administrator";
},
/**
* Check whether a user can create posts for a group or not
*
* @param {Object} info Information about the target group
* @return {boolean} TRUE if the user can create a post / FALSE else
*/
canCreatePosts: function(info){
//Administrator and moderators can always create posts
if(info.membership == "administrator" || info.membership == "moderator")
return true;
if(info.membership == "member" && info.posts_level == "members")
return true;
//In all the other case, the user can not create posts
return false;
},
}

View File

@ -36,14 +36,14 @@ ComunicWeb.components.langPicker = {
class: "modal-header" class: "modal-header"
}); });
var closeModal = createElem2({ var closeModalBtn = createElem2({
appendTo: modalHeader, appendTo: modalHeader,
type: "button", type: "button",
class: "close", class: "close",
}); });
createElem2({ createElem2({
appendTo: closeModal, appendTo: closeModalBtn,
type: "span", type: "span",
innerHTML: "x" innerHTML: "x"
}); });
@ -62,27 +62,27 @@ ComunicWeb.components.langPicker = {
class: "modal-body", class: "modal-body",
}); });
//Display the list of languages //Display the list of language
var langs = [
["fr", "Français"],
["en", "English"]
];
var buttons = []; var buttons = [];
langs.forEach(function(lang){ for(language in ComunicWeb.common.langs.list){
if(typeof language !== "string")
continue;
var info = ComunicWeb.common.langs.list[language];
const button = createElem2({ const button = createElem2({
appendTo: modalBody, appendTo: modalBody,
type: "div", type: "div",
class: "btn btn-default", class: "btn btn-default",
innerHTML: lang[1] innerHTML: info.local_name + " (" + info.name + ")"
}); });
button.setAttribute("data-lang", lang[0]); button.setAttribute("data-lang", language);
buttons.push(button); buttons.push(button);
add_space(modalBody); add_space(modalBody);
}); };
//Modal footer //Modal footer
var modalFooter = createElem2({ var modalFooter = createElem2({
@ -113,7 +113,12 @@ ComunicWeb.components.langPicker = {
closeModal(); closeModal();
//Set the language //Set the language
ComunicWeb.common.langs.setLang(this.getAttribute("data-lang")); var lang = this.getAttribute("data-lang");
ComunicWeb.common.langs.setLang(lang);
//Save settings in user account if signed in
if(signed_in())
ComunicWeb.components.settings.interface.setLanguage(lang);
//Restart the app //Restart the app
ComunicWeb.common.system.reset(); ComunicWeb.common.system.reset();
@ -122,7 +127,7 @@ ComunicWeb.components.langPicker = {
//Make the buttons live //Make the buttons live
cancelButton.onclick = closeModal; cancelButton.onclick = closeModal;
closeModal.onclick = closeModal; closeModalBtn.onclick = closeModal;
//Show the modal //Show the modal
$(modal).modal('show'); $(modal).modal('show');

View File

@ -57,10 +57,10 @@ ComunicWeb.components.like.button = {
else { else {
//Update message //Update message
if(count == 1){ if(count == 1){
likeMsg.innerHTML = "1 like"; likeMsg.innerHTML = lang("like_btn_one_like");
} }
else else
likeMsg.innerHTML = count + " likes"; likeMsg.innerHTML = lang("like_btn_x_likes", [count]);
} }
} }
@ -69,10 +69,10 @@ ComunicWeb.components.like.button = {
//Update the message //Update the message
if(liking == true){ if(liking == true){
likeMsg.innerHTML = "Liking" likeMsg.innerHTML = lang("like_btn_liking")
} }
else { else {
likeMsg.innerHTML = "Like"; likeMsg.innerHTML = lang("like_btn_like");
} }
//Add total count if possible //Add total count if possible

View File

@ -32,6 +32,9 @@ ComunicWeb.components.menuBar.authenticated = {
//Add user name //Add user name
this.addUserName(navbarRightElemList); this.addUserName(navbarRightElemList);
//Alternate latest posts button
this.addAlternateLatestPostsButton(navbarRightElemList);
//Add friends list button //Add friends list button
this.addFriendListButton(navbarRightElemList); this.addFriendListButton(navbarRightElemList);
@ -75,6 +78,35 @@ ComunicWeb.components.menuBar.authenticated = {
dropdownContent.className = "dropdown-menu" dropdownContent.className = "dropdown-menu"
dropdownContent.setAttribute("role", "menu"); dropdownContent.setAttribute("role", "menu");
//Add conversations link
var conversationsButton = createElem2({
appendTo: dropdownContent,
type: "li"
});
var conversationsLink = createElem2({
appendTo: conversationsButton,
type: "a",
innerLang: "menu_bar_action_conversations"
});
conversationsButton.onclick = function(){
openPage("conversations");
};
//Add groups link
var groupsButton = createElem2({
appendTo: dropdownContent,
type: "li"
});
createElem2({
appendTo: groupsButton,
type: "a",
innerLang: "menu_bar_action_groups"
});
groupsButton.onclick = function(){
openPage("groups");
}
//Add settings link //Add settings link
var settingsButton = createElem2({ var settingsButton = createElem2({
appendTo: dropdownContent, appendTo: dropdownContent,
@ -83,7 +115,7 @@ ComunicWeb.components.menuBar.authenticated = {
var settingsLink = createElem2({ var settingsLink = createElem2({
appendTo: settingsButton, appendTo: settingsButton,
type: "a", type: "a",
innerHTML: "Settings" innerLang: "menu_bar_action_settings"
}); });
settingsButton.onclick = function(){ settingsButton.onclick = function(){
openPage("settings"); openPage("settings");
@ -99,6 +131,34 @@ ComunicWeb.components.menuBar.authenticated = {
return dropdownContent; return dropdownContent;
}, },
/**
* Add alternate latest posts button
* (if the screen is too small to display "Comunic")
*
* @param {HTMLElement} target The target for the ubutton
*/
addAlternateLatestPostsButton: function(target){
//Create button
var button = createElem2({
type: "li",
appendTo: target,
class: "alternate-latest-posts-button"
});
//Create link
var link = createElem("a", button);
createElem2({
type: "i",
appendTo: link,
class: "fa fa-history"
});
//Makes link lives
link.addEventListener("click", function(){
openPage("latest");
});
},
/** /**
* Add friendsList toggle button * Add friendsList toggle button
* *
@ -157,7 +217,7 @@ ComunicWeb.components.menuBar.authenticated = {
userimage.src = userInfos.accountImage; userimage.src = userInfos.accountImage;
userlinkelement.onclick = function(){ userlinkelement.onclick = function(){
openUserPage(userIDorPath(userInfos)); openUserPage(userInfos);
} }
}), true); }), true);
@ -170,9 +230,10 @@ ComunicWeb.components.menuBar.authenticated = {
* @return {HTMLElement} The user element * @return {HTMLElement} The user element
*/ */
addSearchForm: function(navbarElem){ addSearchForm: function(navbarElem){
//Create form element
//Create form element (large screens)
var searchForm = createElem("li", navbarElem); var searchForm = createElem("li", navbarElem);
searchForm.className = "dropdown navbar-form navbar-left messages-menu"; searchForm.className = "dropdown navbar-form navbar-left messages-menu hidden-xs";
searchForm.setAttribute("role", "search"); searchForm.setAttribute("role", "search");
//Create form group //Create form group
@ -191,6 +252,24 @@ ComunicWeb.components.menuBar.authenticated = {
dropdownContainer.className = "dropdown-menu"; dropdownContainer.className = "dropdown-menu";
//Initializate menu //Initializate menu
ComunicWeb.components.searchForm.init(searchInput, dropdownContainer); ComunicWeb.components.search.form.init(searchInput, dropdownContainer);
//Create search link (small screen)
var searchLinkLi = createElem2({
appendTo: navbarElem,
type: "li",
class: "navbar-left messages-menu visible-xs"
});
var searchLink = createElem2({
appendTo: searchLinkLi,
type: "a",
innerHTML: "Search"
});
searchLink.addEventListener("click", function(){
openPage("search");
});
} }
}; };

View File

@ -91,14 +91,12 @@ ComunicWeb.components.menuBar.common = {
var navbarCollapsIcon = createElem("i", navbarCollapsedButton); var navbarCollapsIcon = createElem("i", navbarCollapsedButton);
navbarCollapsIcon.className = "fa fa-bars"; navbarCollapsIcon.className = "fa fa-bars";
//Now we need to know if user is logged in or not
var userLoggedIn = ComunicWeb.user.userLogin.getUserLoginState();
//Save login information in menubar before continuing //Save login information in menubar before continuing
menuContainer.setAttribute("forActiveUser", userLoggedIn); menuContainer.setAttribute("forActiveUser", signed_in());
//Call specific menu //Call specific menu
if(userLoggedIn){ if(signed_in()){
//Call authenticated menubar //Call authenticated menubar
ComunicWeb.components.menuBar.authenticated.addElements(containerElem); ComunicWeb.components.menuBar.authenticated.addElements(containerElem);
} }

View File

@ -68,7 +68,7 @@ ComunicWeb.components.notifications.dropdown = {
appendTo: dropdownMenu, appendTo: dropdownMenu,
type: "li", type: "li",
class: "header", class: "header",
innerHTML: "Notifications" innerHTML: lang("notifications_dropdown_title")
}); });
//Add notifications list //Add notifications list
@ -93,13 +93,13 @@ ComunicWeb.components.notifications.dropdown = {
var deleteAllLink = createElem2({ var deleteAllLink = createElem2({
appendTo: dropdownBottom, appendTo: dropdownBottom,
type: "a", type: "a",
innerHTML: "Delete all" innerHTML: lang("notifications_dropdown_delete_all_link")
}); });
//Make the delete all notifications link lives //Make the delete all notifications link lives
deleteAllLink.onclick = function(){ deleteAllLink.onclick = function(){
ComunicWeb.common.messages.confirm("Are you sure do you want to delete all the notifications ? This operation can not be cancelled !", function(accept){ ComunicWeb.common.messages.confirm(lang("notifications_dropdown_confirm_delete_all"), function(accept){
//We continue only if the user confirmed the operation //We continue only if the user confirmed the operation
if(!accept) if(!accept)
@ -110,12 +110,12 @@ ComunicWeb.components.notifications.dropdown = {
//Check for errors //Check for errors
if(result.error){ if(result.error){
ComunicWeb.common.notificationSystem.showNotification("An error occured while trying to delete all the notifications !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("notifications_dropdown_err_delete_all_notifications"), "danger");
return; return;
} }
//Display success //Display success
ComunicWeb.common.notificationSystem.showNotification("The entire list of notification has been cleared.", "success"); ComunicWeb.common.notificationSystem.showNotification(lang("notifications_dropdown_delete_all_success"), "success");
}); });
@ -150,7 +150,7 @@ ComunicWeb.components.notifications.dropdown = {
//Check for errors //Check for errors
if(result.error){ if(result.error){
ComunicWeb.common.notificationSystem.showNotification("An error occured while trying to retrieve notifications list !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("notifications_dropdown_err_get_notifs_list"), "danger");
return; return;
} }
@ -162,28 +162,38 @@ ComunicWeb.components.notifications.dropdown = {
//Check for errors //Check for errors
if(users.error){ if(users.error){
ComunicWeb.common.notificationSystem.showNotification("An error occured while trying to retrieve users informations for the notifications !", "danger"); ComunicWeb.common.notificationSystem.showNotification(lang("notifications_dropdown_err_get_related_users_info"), "danger");
return; return;
} }
//Get information about the groups
var groups_id = ComunicWeb.components.notifications.utils.get_groups_id(result);
getInfoMultipleGroups(groups_id, function(groups){
//Empty the target list //Check for errors
list.innerHTML = ""; if(groups.error)
return notify(lang("notifications_dropdown_err_get_related_groups_info"), "danger");
//Process the list of notifications //Empty the target list
for (var i = 0; i < result.length; i++) { list.innerHTML = "";
const notification = result[i];
//Display the notification
ComunicWeb.components.notifications.ui.display_notification(notification, list, users);
}
//Display a message if there isn't any notification to display //Process the list of notifications
if(result.length == 0){ for (var i = 0; i < result.length; i++) {
const notification = result[i];
//Display the notification
ComunicWeb.components.notifications.ui.display_notification(notification, list, users, groups);
}
list.innerHTML = "<li class='no-notification-msg'>You do not have any notification yet.</li>"; //Display a message if there isn't any notification to display
if(result.length == 0){
} list.innerHTML = "<li class='no-notification-msg'>" + lang("notifications_dropdown_no_notif_notice") + "</li>";
}
});
}, false); }, false);

View File

@ -13,8 +13,9 @@ ComunicWeb.components.notifications.ui = {
* @param {HTMLElement} target The target of the notification * @param {HTMLElement} target The target of the notification
* @param {Object} users Informations about users that might be required * @param {Object} users Informations about users that might be required
* to display the notification * to display the notification
* @param {Object} groups Information about the potential related groups
*/ */
display_notification: function(data, target, users){ display_notification: function(data, target, users, groups){
//Generate the informations about the notifications //Generate the informations about the notifications
var from_user = users["user-"+data.from_user_id]; var from_user = users["user-"+data.from_user_id];
@ -36,6 +37,20 @@ ComunicWeb.components.notifications.ui = {
message += "accepted your friendship request."; message += "accepted your friendship request.";
if(data.type == "rejected_friend_request") if(data.type == "rejected_friend_request")
message += "rejected your friendship request."; message += "rejected your friendship request.";
//About group membership
if(data.type == "sent_group_membership_invitation")
message += "invited you to join the group";
if(data.type == "accepted_group_membership_invitation")
message += "accepted his invitation to join the group";
if(data.type == "rejected_group_membership_invitation")
message += "rejected his invitation to join the group";
if(data.type == "sent_group_membership_request")
message += "sent a request to join the group";
if(data.type == "accepted_group_membership_request")
message += "accepted your request to join the group";
if(data.type == "rejected_group_membership_request")
message += "rejected your request to join the group";
//Generic element creation //Generic element creation
if(data.type == "elem_created"){ if(data.type == "elem_created"){
@ -49,6 +64,7 @@ ComunicWeb.components.notifications.ui = {
message += " "; message += " ";
//Notification target //Notification target
//User page
if(data.from_container_type == "user_page"){ if(data.from_container_type == "user_page"){
if(data.from_user_id == data.from_container_id) if(data.from_user_id == data.from_container_id)
@ -58,6 +74,15 @@ ComunicWeb.components.notifications.ui = {
} }
//Group page
if(data.from_container_type == "group_page"){
message += "on the group "+groups[data.from_container_id].name+".";
}
//Group membership
if(data.on_elem_type == "group_membership")
message += groups[data.on_elem_id].name;
//Create notification action //Create notification action
var action = function(){ var action = function(){
@ -77,7 +102,18 @@ ComunicWeb.components.notifications.ui = {
if(data.on_elem_type == "friend_request"){ if(data.on_elem_type == "friend_request"){
//Open user page //Open user page
openUserPage(data.from_user_id); openUserPageFromID(data.from_user_id);
}
//For the group membership
if(data.on_elem_type == "group_membership"){
//Open appropriate page
if(data.type == "sent_group_membership_request")
openPage("groups/"+data.on_elem_id+"/members");
else
openGroupPage(groups[data.on_elem_id]);
} }

Some files were not shown because too many files have changed in this diff Show More