Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Eugene Shen
erp5
Commits
edcd20f8
Commit
edcd20f8
authored
Mar 28, 2017
by
Eugene Shen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
First commit of all OfficeJS changes in March
parent
39afba4f
Changes
53
Hide whitespace changes
Inline
Side-by-side
Showing
53 changed files
with
12913 additions
and
0 deletions
+12913
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/adapter_js.js
...icejs_eyqs/PathTemplateItem/web_page_module/adapter_js.js
+2756
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/adapter_js.xml
...cejs_eyqs/PathTemplateItem/web_page_module/adapter_js.xml
+119
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/fast_priority_queue_js.js
...athTemplateItem/web_page_module/fast_priority_queue_js.js
+179
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/fast_priority_queue_js.xml
...thTemplateItem/web_page_module/fast_priority_queue_js.xml
+123
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_html.html
...lateItem/web_page_module/gadget_erp5_chat_panel_html.html
+49
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_html.xml
...plateItem/web_page_module/gadget_erp5_chat_panel_html.xml
+128
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_js.js
...TemplateItem/web_page_module/gadget_erp5_chat_panel_js.js
+55
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_js.xml
...emplateItem/web_page_module/gadget_erp5_chat_panel_js.xml
+124
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_html.html
...ateItem/web_page_module/gadget_erp5_chat_webrtc_html.html
+64
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_html.xml
...lateItem/web_page_module/gadget_erp5_chat_webrtc_html.xml
+128
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_js.js
...emplateItem/web_page_module/gadget_erp5_chat_webrtc_js.js
+878
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_js.xml
...mplateItem/web_page_module/gadget_erp5_chat_webrtc_js.xml
+124
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_nojquery_css.css
...TemplateItem/web_page_module/gadget_erp5_nojquery_css.css
+3672
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_nojquery_css.xml
...TemplateItem/web_page_module/gadget_erp5_nojquery_css.xml
+125
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_html.html
...eItem/web_page_module/gadget_erp5_page_chat_box_html.html
+52
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_html.xml
...teItem/web_page_module/gadget_erp5_page_chat_box_html.xml
+128
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_js.js
...plateItem/web_page_module/gadget_erp5_page_chat_box_js.js
+1183
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_js.xml
...lateItem/web_page_module/gadget_erp5_page_chat_box_js.xml
+124
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_html.html
...m/web_page_module/gadget_erp5_page_chat_connect_html.html
+27
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_html.xml
...em/web_page_module/gadget_erp5_page_chat_connect_html.xml
+123
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_js.js
...eItem/web_page_module/gadget_erp5_page_chat_connect_js.js
+82
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_js.xml
...Item/web_page_module/gadget_erp5_page_chat_connect_js.xml
+119
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_html.html
...em/web_page_module/gadget_erp5_page_contact_add_html.html
+13
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_html.xml
...tem/web_page_module/gadget_erp5_page_contact_add_html.xml
+148
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_js.js
...teItem/web_page_module/gadget_erp5_page_contact_add_js.js
+48
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_js.xml
...eItem/web_page_module/gadget_erp5_page_contact_add_js.xml
+144
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_html.html
...m/web_page_module/gadget_erp5_page_contact_list_html.html
+16
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_html.xml
...em/web_page_module/gadget_erp5_page_contact_list_html.xml
+155
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_js.js
...eItem/web_page_module/gadget_erp5_page_contact_list_js.js
+104
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_js.xml
...Item/web_page_module/gadget_erp5_page_contact_list_js.xml
+150
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_html.html
...ge_module/gadget_erp5_page_jio_dav_configurator_html.html
+35
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_html.xml
...age_module/gadget_erp5_page_jio_dav_configurator_html.xml
+127
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_js.js
...b_page_module/gadget_erp5_page_jio_dav_configurator_js.js
+158
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_js.xml
..._page_module/gadget_erp5_page_jio_dav_configurator_js.xml
+123
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_document_configurator_html.html
...dule/gadget_erp5_page_jio_document_configurator_html.html
+30
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_document_configurator_html.xml
...odule/gadget_erp5_page_jio_document_configurator_html.xml
+123
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_html.html
...e_module/gadget_erp5_page_jio_erp5_configurator_html.html
+34
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_html.xml
...ge_module/gadget_erp5_page_jio_erp5_configurator_html.xml
+123
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_js.js
..._page_module/gadget_erp5_page_jio_erp5_configurator_js.js
+153
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_js.xml
...page_module/gadget_erp5_page_jio_erp5_configurator_js.xml
+119
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_html.html
...eb_page_module/gadget_erp5_page_jio_person_view_html.html
+28
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_html.xml
...web_page_module/gadget_erp5_page_jio_person_view_html.xml
+127
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_js.js
...em/web_page_module/gadget_erp5_page_jio_person_view_js.js
+108
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_js.xml
...m/web_page_module/gadget_erp5_page_jio_person_view_js.xml
+123
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_html.html
...plateItem/web_page_module/gadget_erp5_page_sync_html.html
+25
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_html.xml
...mplateItem/web_page_module/gadget_erp5_page_sync_html.xml
+127
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_js.js
...hTemplateItem/web_page_module/gadget_erp5_page_sync_js.js
+69
-0
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_js.xml
...TemplateItem/web_page_module/gadget_erp5_page_sync_js.xml
+123
-0
bt5/officejs_eyqs/bt/dependency_list
bt5/officejs_eyqs/bt/dependency_list
+3
-0
bt5/officejs_eyqs/bt/maintainer_list
bt5/officejs_eyqs/bt/maintainer_list
+1
-0
bt5/officejs_eyqs/bt/template_format_version
bt5/officejs_eyqs/bt/template_format_version
+1
-0
bt5/officejs_eyqs/bt/template_path_list
bt5/officejs_eyqs/bt/template_path_list
+12
-0
bt5/officejs_eyqs/bt/title
bt5/officejs_eyqs/bt/title
+1
-0
No files found.
bt5/officejs_eyqs/PathTemplateItem/web_page_module/adapter_js.js
0 → 100644
View file @
edcd20f8
(
function
(
f
){
if
(
typeof
exports
===
"
object
"
&&
typeof
module
!==
"
undefined
"
){
module
.
exports
=
f
()}
else
if
(
typeof
define
===
"
function
"
&&
define
.
amd
){
define
([],
f
)}
else
{
var
g
;
if
(
typeof
window
!==
"
undefined
"
){
g
=
window
}
else
if
(
typeof
global
!==
"
undefined
"
){
g
=
global
}
else
if
(
typeof
self
!==
"
undefined
"
){
g
=
self
}
else
{
g
=
this
}
g
.
adapter
=
f
()}})(
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
);
var
f
=
new
Error
(
"
Cannot find module '
"
+
o
+
"
'
"
);
throw
f
.
code
=
"
MODULE_NOT_FOUND
"
,
f
}
var
l
=
n
[
o
]
=
{
exports
:{}};
t
[
o
][
0
].
call
(
l
.
exports
,
function
(
e
){
var
n
=
t
[
o
][
1
][
e
];
return
s
(
n
?
n
:
e
)},
l
,
l
.
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
){
/* eslint-env node */
'
use strict
'
;
// SDP helpers.
var
SDPUtils
=
{};
// Generate an alphanumeric identifier for cname or mids.
// TODO: use UUIDs instead? https://gist.github.com/jed/982883
SDPUtils
.
generateIdentifier
=
function
()
{
return
Math
.
random
().
toString
(
36
).
substr
(
2
,
10
);
};
// The RTCP CNAME used by all peerconnections from the same JS.
SDPUtils
.
localCName
=
SDPUtils
.
generateIdentifier
();
// Splits SDP into lines, dealing with both CRLF and LF.
SDPUtils
.
splitLines
=
function
(
blob
)
{
return
blob
.
trim
().
split
(
'
\n
'
).
map
(
function
(
line
)
{
return
line
.
trim
();
});
};
// Splits SDP into sessionpart and mediasections. Ensures CRLF.
SDPUtils
.
splitSections
=
function
(
blob
)
{
var
parts
=
blob
.
split
(
'
\n
m=
'
);
return
parts
.
map
(
function
(
part
,
index
)
{
return
(
index
>
0
?
'
m=
'
+
part
:
part
).
trim
()
+
'
\r\n
'
;
});
};
// Returns lines that start with a certain prefix.
SDPUtils
.
matchPrefix
=
function
(
blob
,
prefix
)
{
return
SDPUtils
.
splitLines
(
blob
).
filter
(
function
(
line
)
{
return
line
.
indexOf
(
prefix
)
===
0
;
});
};
// Parses an ICE candidate line. Sample input:
// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
// rport 55996"
SDPUtils
.
parseCandidate
=
function
(
line
)
{
var
parts
;
// Parse both variants.
if
(
line
.
indexOf
(
'
a=candidate:
'
)
===
0
)
{
parts
=
line
.
substring
(
12
).
split
(
'
'
);
}
else
{
parts
=
line
.
substring
(
10
).
split
(
'
'
);
}
var
candidate
=
{
foundation
:
parts
[
0
],
component
:
parts
[
1
],
protocol
:
parts
[
2
].
toLowerCase
(),
priority
:
parseInt
(
parts
[
3
],
10
),
ip
:
parts
[
4
],
port
:
parseInt
(
parts
[
5
],
10
),
// skip parts[6] == 'typ'
type
:
parts
[
7
]
};
for
(
var
i
=
8
;
i
<
parts
.
length
;
i
+=
2
)
{
switch
(
parts
[
i
])
{
case
'
raddr
'
:
candidate
.
relatedAddress
=
parts
[
i
+
1
];
break
;
case
'
rport
'
:
candidate
.
relatedPort
=
parseInt
(
parts
[
i
+
1
],
10
);
break
;
case
'
tcptype
'
:
candidate
.
tcpType
=
parts
[
i
+
1
];
break
;
default
:
// Unknown extensions are silently ignored.
break
;
}
}
return
candidate
;
};
// Translates a candidate object into SDP candidate attribute.
SDPUtils
.
writeCandidate
=
function
(
candidate
)
{
var
sdp
=
[];
sdp
.
push
(
candidate
.
foundation
);
sdp
.
push
(
candidate
.
component
);
sdp
.
push
(
candidate
.
protocol
.
toUpperCase
());
sdp
.
push
(
candidate
.
priority
);
sdp
.
push
(
candidate
.
ip
);
sdp
.
push
(
candidate
.
port
);
var
type
=
candidate
.
type
;
sdp
.
push
(
'
typ
'
);
sdp
.
push
(
type
);
if
(
type
!==
'
host
'
&&
candidate
.
relatedAddress
&&
candidate
.
relatedPort
)
{
sdp
.
push
(
'
raddr
'
);
sdp
.
push
(
candidate
.
relatedAddress
);
// was: relAddr
sdp
.
push
(
'
rport
'
);
sdp
.
push
(
candidate
.
relatedPort
);
// was: relPort
}
if
(
candidate
.
tcpType
&&
candidate
.
protocol
.
toLowerCase
()
===
'
tcp
'
)
{
sdp
.
push
(
'
tcptype
'
);
sdp
.
push
(
candidate
.
tcpType
);
}
return
'
candidate:
'
+
sdp
.
join
(
'
'
);
};
// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
// a=rtpmap:111 opus/48000/2
SDPUtils
.
parseRtpMap
=
function
(
line
)
{
var
parts
=
line
.
substr
(
9
).
split
(
'
'
);
var
parsed
=
{
payloadType
:
parseInt
(
parts
.
shift
(),
10
)
// was: id
};
parts
=
parts
[
0
].
split
(
'
/
'
);
parsed
.
name
=
parts
[
0
];
parsed
.
clockRate
=
parseInt
(
parts
[
1
],
10
);
// was: clockrate
// was: channels
parsed
.
numChannels
=
parts
.
length
===
3
?
parseInt
(
parts
[
2
],
10
)
:
1
;
return
parsed
;
};
// Generate an a=rtpmap line from RTCRtpCodecCapability or
// RTCRtpCodecParameters.
SDPUtils
.
writeRtpMap
=
function
(
codec
)
{
var
pt
=
codec
.
payloadType
;
if
(
codec
.
preferredPayloadType
!==
undefined
)
{
pt
=
codec
.
preferredPayloadType
;
}
return
'
a=rtpmap:
'
+
pt
+
'
'
+
codec
.
name
+
'
/
'
+
codec
.
clockRate
+
(
codec
.
numChannels
!==
1
?
'
/
'
+
codec
.
numChannels
:
''
)
+
'
\r\n
'
;
};
// Parses an a=extmap line (headerextension from RFC 5285). Sample input:
// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
SDPUtils
.
parseExtmap
=
function
(
line
)
{
var
parts
=
line
.
substr
(
9
).
split
(
'
'
);
return
{
id
:
parseInt
(
parts
[
0
],
10
),
uri
:
parts
[
1
]
};
};
// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
// RTCRtpHeaderExtension.
SDPUtils
.
writeExtmap
=
function
(
headerExtension
)
{
return
'
a=extmap:
'
+
(
headerExtension
.
id
||
headerExtension
.
preferredId
)
+
'
'
+
headerExtension
.
uri
+
'
\r\n
'
;
};
// Parses an ftmp line, returns dictionary. Sample input:
// a=fmtp:96 vbr=on;cng=on
// Also deals with vbr=on; cng=on
SDPUtils
.
parseFmtp
=
function
(
line
)
{
var
parsed
=
{};
var
kv
;
var
parts
=
line
.
substr
(
line
.
indexOf
(
'
'
)
+
1
).
split
(
'
;
'
);
for
(
var
j
=
0
;
j
<
parts
.
length
;
j
++
)
{
kv
=
parts
[
j
].
trim
().
split
(
'
=
'
);
parsed
[
kv
[
0
].
trim
()]
=
kv
[
1
];
}
return
parsed
;
};
// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils
.
writeFmtp
=
function
(
codec
)
{
var
line
=
''
;
var
pt
=
codec
.
payloadType
;
if
(
codec
.
preferredPayloadType
!==
undefined
)
{
pt
=
codec
.
preferredPayloadType
;
}
if
(
codec
.
parameters
&&
Object
.
keys
(
codec
.
parameters
).
length
)
{
var
params
=
[];
Object
.
keys
(
codec
.
parameters
).
forEach
(
function
(
param
)
{
params
.
push
(
param
+
'
=
'
+
codec
.
parameters
[
param
]);
});
line
+=
'
a=fmtp:
'
+
pt
+
'
'
+
params
.
join
(
'
;
'
)
+
'
\r\n
'
;
}
return
line
;
};
// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
// a=rtcp-fb:98 nack rpsi
SDPUtils
.
parseRtcpFb
=
function
(
line
)
{
var
parts
=
line
.
substr
(
line
.
indexOf
(
'
'
)
+
1
).
split
(
'
'
);
return
{
type
:
parts
.
shift
(),
parameter
:
parts
.
join
(
'
'
)
};
};
// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils
.
writeRtcpFb
=
function
(
codec
)
{
var
lines
=
''
;
var
pt
=
codec
.
payloadType
;
if
(
codec
.
preferredPayloadType
!==
undefined
)
{
pt
=
codec
.
preferredPayloadType
;
}
if
(
codec
.
rtcpFeedback
&&
codec
.
rtcpFeedback
.
length
)
{
// FIXME: special handling for trr-int?
codec
.
rtcpFeedback
.
forEach
(
function
(
fb
)
{
lines
+=
'
a=rtcp-fb:
'
+
pt
+
'
'
+
fb
.
type
+
(
fb
.
parameter
&&
fb
.
parameter
.
length
?
'
'
+
fb
.
parameter
:
''
)
+
'
\r\n
'
;
});
}
return
lines
;
};
// Parses an RFC 5576 ssrc media attribute. Sample input:
// a=ssrc:3735928559 cname:something
SDPUtils
.
parseSsrcMedia
=
function
(
line
)
{
var
sp
=
line
.
indexOf
(
'
'
);
var
parts
=
{
ssrc
:
parseInt
(
line
.
substr
(
7
,
sp
-
7
),
10
)
};
var
colon
=
line
.
indexOf
(
'
:
'
,
sp
);
if
(
colon
>
-
1
)
{
parts
.
attribute
=
line
.
substr
(
sp
+
1
,
colon
-
sp
-
1
);
parts
.
value
=
line
.
substr
(
colon
+
1
);
}
else
{
parts
.
attribute
=
line
.
substr
(
sp
+
1
);
}
return
parts
;
};
// Extracts DTLS parameters from SDP media section or sessionpart.
// FIXME: for consistency with other functions this should only
// get the fingerprint line as input. See also getIceParameters.
SDPUtils
.
getDtlsParameters
=
function
(
mediaSection
,
sessionpart
)
{
var
lines
=
SDPUtils
.
splitLines
(
mediaSection
);
// Search in session part, too.
lines
=
lines
.
concat
(
SDPUtils
.
splitLines
(
sessionpart
));
var
fpLine
=
lines
.
filter
(
function
(
line
)
{
return
line
.
indexOf
(
'
a=fingerprint:
'
)
===
0
;
})[
0
].
substr
(
14
);
// Note: a=setup line is ignored since we use the 'auto' role.
var
dtlsParameters
=
{
role
:
'
auto
'
,
fingerprints
:
[{
algorithm
:
fpLine
.
split
(
'
'
)[
0
],
value
:
fpLine
.
split
(
'
'
)[
1
]
}]
};
return
dtlsParameters
;
};
// Serializes DTLS parameters to SDP.
SDPUtils
.
writeDtlsParameters
=
function
(
params
,
setupType
)
{
var
sdp
=
'
a=setup:
'
+
setupType
+
'
\r\n
'
;
params
.
fingerprints
.
forEach
(
function
(
fp
)
{
sdp
+=
'
a=fingerprint:
'
+
fp
.
algorithm
+
'
'
+
fp
.
value
+
'
\r\n
'
;
});
return
sdp
;
};
// Parses ICE information from SDP media section or sessionpart.
// FIXME: for consistency with other functions this should only
// get the ice-ufrag and ice-pwd lines as input.
SDPUtils
.
getIceParameters
=
function
(
mediaSection
,
sessionpart
)
{
var
lines
=
SDPUtils
.
splitLines
(
mediaSection
);
// Search in session part, too.
lines
=
lines
.
concat
(
SDPUtils
.
splitLines
(
sessionpart
));
var
iceParameters
=
{
usernameFragment
:
lines
.
filter
(
function
(
line
)
{
return
line
.
indexOf
(
'
a=ice-ufrag:
'
)
===
0
;
})[
0
].
substr
(
12
),
password
:
lines
.
filter
(
function
(
line
)
{
return
line
.
indexOf
(
'
a=ice-pwd:
'
)
===
0
;
})[
0
].
substr
(
10
)
};
return
iceParameters
;
};
// Serializes ICE parameters to SDP.
SDPUtils
.
writeIceParameters
=
function
(
params
)
{
return
'
a=ice-ufrag:
'
+
params
.
usernameFragment
+
'
\r\n
'
+
'
a=ice-pwd:
'
+
params
.
password
+
'
\r\n
'
;
};
// Parses the SDP media section and returns RTCRtpParameters.
SDPUtils
.
parseRtpParameters
=
function
(
mediaSection
)
{
var
description
=
{
codecs
:
[],
headerExtensions
:
[],
fecMechanisms
:
[],
rtcp
:
[]
};
var
lines
=
SDPUtils
.
splitLines
(
mediaSection
);
var
mline
=
lines
[
0
].
split
(
'
'
);
for
(
var
i
=
3
;
i
<
mline
.
length
;
i
++
)
{
// find all codecs from mline[3..]
var
pt
=
mline
[
i
];
var
rtpmapline
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=rtpmap:
'
+
pt
+
'
'
)[
0
];
if
(
rtpmapline
)
{
var
codec
=
SDPUtils
.
parseRtpMap
(
rtpmapline
);
var
fmtps
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=fmtp:
'
+
pt
+
'
'
);
// Only the first a=fmtp:<pt> is considered.
codec
.
parameters
=
fmtps
.
length
?
SDPUtils
.
parseFmtp
(
fmtps
[
0
])
:
{};
codec
.
rtcpFeedback
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=rtcp-fb:
'
+
pt
+
'
'
)
.
map
(
SDPUtils
.
parseRtcpFb
);
description
.
codecs
.
push
(
codec
);
// parse FEC mechanisms from rtpmap lines.
switch
(
codec
.
name
.
toUpperCase
())
{
case
'
RED
'
:
case
'
ULPFEC
'
:
description
.
fecMechanisms
.
push
(
codec
.
name
.
toUpperCase
());
break
;
default
:
// only RED and ULPFEC are recognized as FEC mechanisms.
break
;
}
}
}
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=extmap:
'
).
forEach
(
function
(
line
)
{
description
.
headerExtensions
.
push
(
SDPUtils
.
parseExtmap
(
line
));
});
// FIXME: parse rtcp.
return
description
;
};
// Generates parts of the SDP media section describing the capabilities /
// parameters.
SDPUtils
.
writeRtpDescription
=
function
(
kind
,
caps
)
{
var
sdp
=
''
;
// Build the mline.
sdp
+=
'
m=
'
+
kind
+
'
'
;
sdp
+=
caps
.
codecs
.
length
>
0
?
'
9
'
:
'
0
'
;
// reject if no codecs.
sdp
+=
'
UDP/TLS/RTP/SAVPF
'
;
sdp
+=
caps
.
codecs
.
map
(
function
(
codec
)
{
if
(
codec
.
preferredPayloadType
!==
undefined
)
{
return
codec
.
preferredPayloadType
;
}
return
codec
.
payloadType
;
}).
join
(
'
'
)
+
'
\r\n
'
;
sdp
+=
'
c=IN IP4 0.0.0.0
\r\n
'
;
sdp
+=
'
a=rtcp:9 IN IP4 0.0.0.0
\r\n
'
;
// Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
caps
.
codecs
.
forEach
(
function
(
codec
)
{
sdp
+=
SDPUtils
.
writeRtpMap
(
codec
);
sdp
+=
SDPUtils
.
writeFmtp
(
codec
);
sdp
+=
SDPUtils
.
writeRtcpFb
(
codec
);
});
sdp
+=
'
a=rtcp-mux
\r\n
'
;
caps
.
headerExtensions
.
forEach
(
function
(
extension
)
{
sdp
+=
SDPUtils
.
writeExtmap
(
extension
);
});
// FIXME: write fecMechanisms.
return
sdp
;
};
// Parses the SDP media section and returns an array of
// RTCRtpEncodingParameters.
SDPUtils
.
parseRtpEncodingParameters
=
function
(
mediaSection
)
{
var
encodingParameters
=
[];
var
description
=
SDPUtils
.
parseRtpParameters
(
mediaSection
);
var
hasRed
=
description
.
fecMechanisms
.
indexOf
(
'
RED
'
)
!==
-
1
;
var
hasUlpfec
=
description
.
fecMechanisms
.
indexOf
(
'
ULPFEC
'
)
!==
-
1
;
// filter a=ssrc:... cname:, ignore PlanB-msid
var
ssrcs
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=ssrc:
'
)
.
map
(
function
(
line
)
{
return
SDPUtils
.
parseSsrcMedia
(
line
);
})
.
filter
(
function
(
parts
)
{
return
parts
.
attribute
===
'
cname
'
;
});
var
primarySsrc
=
ssrcs
.
length
>
0
&&
ssrcs
[
0
].
ssrc
;
var
secondarySsrc
;
var
flows
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=ssrc-group:FID
'
)
.
map
(
function
(
line
)
{
var
parts
=
line
.
split
(
'
'
);
parts
.
shift
();
return
parts
.
map
(
function
(
part
)
{
return
parseInt
(
part
,
10
);
});
});
if
(
flows
.
length
>
0
&&
flows
[
0
].
length
>
1
&&
flows
[
0
][
0
]
===
primarySsrc
)
{
secondarySsrc
=
flows
[
0
][
1
];
}
description
.
codecs
.
forEach
(
function
(
codec
)
{
if
(
codec
.
name
.
toUpperCase
()
===
'
RTX
'
&&
codec
.
parameters
.
apt
)
{
var
encParam
=
{
ssrc
:
primarySsrc
,
codecPayloadType
:
parseInt
(
codec
.
parameters
.
apt
,
10
),
rtx
:
{
payloadType
:
codec
.
payloadType
,
ssrc
:
secondarySsrc
}
};
encodingParameters
.
push
(
encParam
);
if
(
hasRed
)
{
encParam
=
JSON
.
parse
(
JSON
.
stringify
(
encParam
));
encParam
.
fec
=
{
ssrc
:
secondarySsrc
,
mechanism
:
hasUlpfec
?
'
red+ulpfec
'
:
'
red
'
};
encodingParameters
.
push
(
encParam
);
}
}
});
if
(
encodingParameters
.
length
===
0
&&
primarySsrc
)
{
encodingParameters
.
push
({
ssrc
:
primarySsrc
});
}
// we support both b=AS and b=TIAS but interpret AS as TIAS.
var
bandwidth
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
b=
'
);
if
(
bandwidth
.
length
)
{
if
(
bandwidth
[
0
].
indexOf
(
'
b=TIAS:
'
)
===
0
)
{
bandwidth
=
parseInt
(
bandwidth
[
0
].
substr
(
7
),
10
);
}
else
if
(
bandwidth
[
0
].
indexOf
(
'
b=AS:
'
)
===
0
)
{
bandwidth
=
parseInt
(
bandwidth
[
0
].
substr
(
5
),
10
);
}
encodingParameters
.
forEach
(
function
(
params
)
{
params
.
maxBitrate
=
bandwidth
;
});
}
return
encodingParameters
;
};
SDPUtils
.
writeSessionBoilerplate
=
function
()
{
// FIXME: sess-id should be an NTP timestamp.
return
'
v=0
\r\n
'
+
'
o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1
\r\n
'
+
'
s=-
\r\n
'
+
'
t=0 0
\r\n
'
;
};
SDPUtils
.
writeMediaSection
=
function
(
transceiver
,
caps
,
type
,
stream
)
{
var
sdp
=
SDPUtils
.
writeRtpDescription
(
transceiver
.
kind
,
caps
);
// Map ICE parameters (ufrag, pwd) to SDP.
sdp
+=
SDPUtils
.
writeIceParameters
(
transceiver
.
iceGatherer
.
getLocalParameters
());
// Map DTLS parameters to SDP.
sdp
+=
SDPUtils
.
writeDtlsParameters
(
transceiver
.
dtlsTransport
.
getLocalParameters
(),
type
===
'
offer
'
?
'
actpass
'
:
'
active
'
);
sdp
+=
'
a=mid:
'
+
transceiver
.
mid
+
'
\r\n
'
;
if
(
transceiver
.
rtpSender
&&
transceiver
.
rtpReceiver
)
{
sdp
+=
'
a=sendrecv
\r\n
'
;
}
else
if
(
transceiver
.
rtpSender
)
{
sdp
+=
'
a=sendonly
\r\n
'
;
}
else
if
(
transceiver
.
rtpReceiver
)
{
sdp
+=
'
a=recvonly
\r\n
'
;
}
else
{
sdp
+=
'
a=inactive
\r\n
'
;
}
// FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.
if
(
transceiver
.
rtpSender
)
{
var
msid
=
'
msid:
'
+
stream
.
id
+
'
'
+
transceiver
.
rtpSender
.
track
.
id
+
'
\r\n
'
;
sdp
+=
'
a=
'
+
msid
;
sdp
+=
'
a=ssrc:
'
+
transceiver
.
sendEncodingParameters
[
0
].
ssrc
+
'
'
+
msid
;
}
// FIXME: this should be written by writeRtpDescription.
sdp
+=
'
a=ssrc:
'
+
transceiver
.
sendEncodingParameters
[
0
].
ssrc
+
'
cname:
'
+
SDPUtils
.
localCName
+
'
\r\n
'
;
return
sdp
;
};
// Gets the direction from the mediaSection or the sessionpart.
SDPUtils
.
getDirection
=
function
(
mediaSection
,
sessionpart
)
{
// Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
var
lines
=
SDPUtils
.
splitLines
(
mediaSection
);
for
(
var
i
=
0
;
i
<
lines
.
length
;
i
++
)
{
switch
(
lines
[
i
])
{
case
'
a=sendrecv
'
:
case
'
a=sendonly
'
:
case
'
a=recvonly
'
:
case
'
a=inactive
'
:
return
lines
[
i
].
substr
(
2
);
default
:
// FIXME: What should happen here?
}
}
if
(
sessionpart
)
{
return
SDPUtils
.
getDirection
(
sessionpart
);
}
return
'
sendrecv
'
;
};
// Expose public methods.
module
.
exports
=
SDPUtils
;
},{}],
2
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
// Shimming starts here.
(
function
()
{
// Utils.
var
utils
=
require
(
'
./utils
'
);
var
logging
=
utils
.
log
;
var
browserDetails
=
utils
.
browserDetails
;
// Export to the adapter global object visible in the browser.
module
.
exports
.
browserDetails
=
browserDetails
;
module
.
exports
.
extractVersion
=
utils
.
extractVersion
;
module
.
exports
.
disableLog
=
utils
.
disableLog
;
// Uncomment the line below if you want logging to occur, including logging
// for the switch statement below. Can also be turned on in the browser via
// adapter.disableLog(false), but then logging from the switch statement below
// will not appear.
// require('./utils').disableLog(false);
// Browser shims.
var
chromeShim
=
require
(
'
./chrome/chrome_shim
'
)
||
null
;
var
edgeShim
=
require
(
'
./edge/edge_shim
'
)
||
null
;
var
firefoxShim
=
require
(
'
./firefox/firefox_shim
'
)
||
null
;
var
safariShim
=
require
(
'
./safari/safari_shim
'
)
||
null
;
// Shim browser if found.
switch
(
browserDetails
.
browser
)
{
case
'
opera
'
:
// fallthrough as it uses chrome shims
case
'
chrome
'
:
if
(
!
chromeShim
||
!
chromeShim
.
shimPeerConnection
)
{
logging
(
'
Chrome shim is not included in this adapter release.
'
);
return
;
}
logging
(
'
adapter.js shimming chrome.
'
);
// Export to the adapter global object visible in the browser.
module
.
exports
.
browserShim
=
chromeShim
;
chromeShim
.
shimGetUserMedia
();
chromeShim
.
shimMediaStream
();
chromeShim
.
shimSourceObject
();
utils
.
shimCreateObjectURL
();
chromeShim
.
shimPeerConnection
();
chromeShim
.
shimOnTrack
();
break
;
case
'
firefox
'
:
if
(
!
firefoxShim
||
!
firefoxShim
.
shimPeerConnection
)
{
logging
(
'
Firefox shim is not included in this adapter release.
'
);
return
;
}
logging
(
'
adapter.js shimming firefox.
'
);
// Export to the adapter global object visible in the browser.
module
.
exports
.
browserShim
=
firefoxShim
;
firefoxShim
.
shimGetUserMedia
();
utils
.
shimCreateObjectURL
();
firefoxShim
.
shimSourceObject
();
firefoxShim
.
shimPeerConnection
();
firefoxShim
.
shimOnTrack
();
break
;
case
'
edge
'
:
if
(
!
edgeShim
||
!
edgeShim
.
shimPeerConnection
)
{
logging
(
'
MS edge shim is not included in this adapter release.
'
);
return
;
}
logging
(
'
adapter.js shimming edge.
'
);
// Export to the adapter global object visible in the browser.
module
.
exports
.
browserShim
=
edgeShim
;
edgeShim
.
shimGetUserMedia
();
utils
.
shimCreateObjectURL
();
edgeShim
.
shimPeerConnection
();
break
;
case
'
safari
'
:
if
(
!
safariShim
)
{
logging
(
'
Safari shim is not included in this adapter release.
'
);
return
;
}
logging
(
'
adapter.js shimming safari.
'
);
// Export to the adapter global object visible in the browser.
module
.
exports
.
browserShim
=
safariShim
;
safariShim
.
shimGetUserMedia
();
break
;
default
:
logging
(
'
Unsupported browser!
'
);
}
})();
},{
"
./chrome/chrome_shim
"
:
3
,
"
./edge/edge_shim
"
:
5
,
"
./firefox/firefox_shim
"
:
7
,
"
./safari/safari_shim
"
:
9
,
"
./utils
"
:
10
}],
3
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
var
logging
=
require
(
'
../utils.js
'
).
log
;
var
browserDetails
=
require
(
'
../utils.js
'
).
browserDetails
;
var
chromeShim
=
{
shimMediaStream
:
function
()
{
window
.
MediaStream
=
window
.
MediaStream
||
window
.
webkitMediaStream
;
},
shimOnTrack
:
function
()
{
if
(
typeof
window
===
'
object
'
&&
window
.
RTCPeerConnection
&&
!
(
'
ontrack
'
in
window
.
RTCPeerConnection
.
prototype
))
{
Object
.
defineProperty
(
window
.
RTCPeerConnection
.
prototype
,
'
ontrack
'
,
{
get
:
function
()
{
return
this
.
_ontrack
;
},
set
:
function
(
f
)
{
var
self
=
this
;
if
(
this
.
_ontrack
)
{
this
.
removeEventListener
(
'
track
'
,
this
.
_ontrack
);
this
.
removeEventListener
(
'
addstream
'
,
this
.
_ontrackpoly
);
}
this
.
addEventListener
(
'
track
'
,
this
.
_ontrack
=
f
);
this
.
addEventListener
(
'
addstream
'
,
this
.
_ontrackpoly
=
function
(
e
)
{
// onaddstream does not fire when a track is added to an existing
// stream. But stream.onaddtrack is implemented so we use that.
e
.
stream
.
addEventListener
(
'
addtrack
'
,
function
(
te
)
{
var
event
=
new
Event
(
'
track
'
);
event
.
track
=
te
.
track
;
event
.
receiver
=
{
track
:
te
.
track
};
event
.
streams
=
[
e
.
stream
];
self
.
dispatchEvent
(
event
);
});
e
.
stream
.
getTracks
().
forEach
(
function
(
track
)
{
var
event
=
new
Event
(
'
track
'
);
event
.
track
=
track
;
event
.
receiver
=
{
track
:
track
};
event
.
streams
=
[
e
.
stream
];
this
.
dispatchEvent
(
event
);
}.
bind
(
this
));
}.
bind
(
this
));
}
});
}
},
shimSourceObject
:
function
()
{
if
(
typeof
window
===
'
object
'
)
{
if
(
window
.
HTMLMediaElement
&&
!
(
'
srcObject
'
in
window
.
HTMLMediaElement
.
prototype
))
{
// Shim the srcObject property, once, when HTMLMediaElement is found.
Object
.
defineProperty
(
window
.
HTMLMediaElement
.
prototype
,
'
srcObject
'
,
{
get
:
function
()
{
return
this
.
_srcObject
;
},
set
:
function
(
stream
)
{
var
self
=
this
;
// Use _srcObject as a private property for this shim
this
.
_srcObject
=
stream
;
if
(
this
.
src
)
{
URL
.
revokeObjectURL
(
this
.
src
);
}
if
(
!
stream
)
{
this
.
src
=
''
;
return
;
}
this
.
src
=
URL
.
createObjectURL
(
stream
);
// We need to recreate the blob url when a track is added or
// removed. Doing it manually since we want to avoid a recursion.
stream
.
addEventListener
(
'
addtrack
'
,
function
()
{
if
(
self
.
src
)
{
URL
.
revokeObjectURL
(
self
.
src
);
}
self
.
src
=
URL
.
createObjectURL
(
stream
);
});
stream
.
addEventListener
(
'
removetrack
'
,
function
()
{
if
(
self
.
src
)
{
URL
.
revokeObjectURL
(
self
.
src
);
}
self
.
src
=
URL
.
createObjectURL
(
stream
);
});
}
});
}
}
},
shimPeerConnection
:
function
()
{
// The RTCPeerConnection object.
if
(
!
window
.
RTCPeerConnection
)
{
window
.
RTCPeerConnection
=
function
(
pcConfig
,
pcConstraints
)
{
// Translate iceTransportPolicy to iceTransports,
// see https://code.google.com/p/webrtc/issues/detail?id=4869
// this was fixed in M56 along with unprefixing RTCPeerConnection.
logging
(
'
PeerConnection
'
);
if
(
pcConfig
&&
pcConfig
.
iceTransportPolicy
)
{
pcConfig
.
iceTransports
=
pcConfig
.
iceTransportPolicy
;
}
return
new
webkitRTCPeerConnection
(
pcConfig
,
pcConstraints
);
};
window
.
RTCPeerConnection
.
prototype
=
webkitRTCPeerConnection
.
prototype
;
// wrap static methods. Currently just generateCertificate.
if
(
webkitRTCPeerConnection
.
generateCertificate
)
{
Object
.
defineProperty
(
window
.
RTCPeerConnection
,
'
generateCertificate
'
,
{
get
:
function
()
{
return
webkitRTCPeerConnection
.
generateCertificate
;
}
});
}
}
var
origGetStats
=
RTCPeerConnection
.
prototype
.
getStats
;
RTCPeerConnection
.
prototype
.
getStats
=
function
(
selector
,
successCallback
,
errorCallback
)
{
var
self
=
this
;
var
args
=
arguments
;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if
(
arguments
.
length
>
0
&&
typeof
selector
===
'
function
'
)
{
return
origGetStats
.
apply
(
this
,
arguments
);
}
// When spec-style getStats is supported, return those.
if
(
origGetStats
.
length
===
0
)
{
return
origGetStats
.
apply
(
this
,
arguments
);
}
var
fixChromeStats_
=
function
(
response
)
{
var
standardReport
=
{};
var
reports
=
response
.
result
();
reports
.
forEach
(
function
(
report
)
{
var
standardStats
=
{
id
:
report
.
id
,
timestamp
:
report
.
timestamp
,
type
:
{
localcandidate
:
'
local-candidate
'
,
remotecandidate
:
'
remote-candidate
'
}[
report
.
type
]
||
report
.
type
};
report
.
names
().
forEach
(
function
(
name
)
{
standardStats
[
name
]
=
report
.
stat
(
name
);
});
standardReport
[
standardStats
.
id
]
=
standardStats
;
});
return
standardReport
;
};
// shim getStats with maplike support
var
makeMapStats
=
function
(
stats
)
{
return
new
Map
(
Object
.
keys
(
stats
).
map
(
function
(
key
)
{
return
[
key
,
stats
[
key
]];
}));
};
if
(
arguments
.
length
>=
2
)
{
var
successCallbackWrapper_
=
function
(
response
)
{
args
[
1
](
makeMapStats
(
fixChromeStats_
(
response
)));
};
return
origGetStats
.
apply
(
this
,
[
successCallbackWrapper_
,
arguments
[
0
]]);
}
// promise-support
return
new
Promise
(
function
(
resolve
,
reject
)
{
origGetStats
.
apply
(
self
,
[
function
(
response
)
{
resolve
(
makeMapStats
(
fixChromeStats_
(
response
)));
},
reject
]);
}).
then
(
successCallback
,
errorCallback
);
};
// add promise support -- natively available in Chrome 51
if
(
browserDetails
.
version
<
51
)
{
[
'
setLocalDescription
'
,
'
setRemoteDescription
'
,
'
addIceCandidate
'
]
.
forEach
(
function
(
method
)
{
var
nativeMethod
=
RTCPeerConnection
.
prototype
[
method
];
RTCPeerConnection
.
prototype
[
method
]
=
function
()
{
var
args
=
arguments
;
var
self
=
this
;
var
promise
=
new
Promise
(
function
(
resolve
,
reject
)
{
nativeMethod
.
apply
(
self
,
[
args
[
0
],
resolve
,
reject
]);
});
if
(
args
.
length
<
2
)
{
return
promise
;
}
return
promise
.
then
(
function
()
{
args
[
1
].
apply
(
null
,
[]);
},
function
(
err
)
{
if
(
args
.
length
>=
3
)
{
args
[
2
].
apply
(
null
,
[
err
]);
}
});
};
});
}
// promise support for createOffer and createAnswer. Available (without
// bugs) since M52: crbug/619289
if
(
browserDetails
.
version
<
52
)
{
[
'
createOffer
'
,
'
createAnswer
'
].
forEach
(
function
(
method
)
{
var
nativeMethod
=
RTCPeerConnection
.
prototype
[
method
];
RTCPeerConnection
.
prototype
[
method
]
=
function
()
{
var
self
=
this
;
if
(
arguments
.
length
<
1
||
(
arguments
.
length
===
1
&&
typeof
arguments
[
0
]
===
'
object
'
))
{
var
opts
=
arguments
.
length
===
1
?
arguments
[
0
]
:
undefined
;
return
new
Promise
(
function
(
resolve
,
reject
)
{
nativeMethod
.
apply
(
self
,
[
resolve
,
reject
,
opts
]);
});
}
return
nativeMethod
.
apply
(
this
,
arguments
);
};
});
}
// shim implicit creation of RTCSessionDescription/RTCIceCandidate
[
'
setLocalDescription
'
,
'
setRemoteDescription
'
,
'
addIceCandidate
'
]
.
forEach
(
function
(
method
)
{
var
nativeMethod
=
RTCPeerConnection
.
prototype
[
method
];
RTCPeerConnection
.
prototype
[
method
]
=
function
()
{
arguments
[
0
]
=
new
((
method
===
'
addIceCandidate
'
)
?
RTCIceCandidate
:
RTCSessionDescription
)(
arguments
[
0
]);
return
nativeMethod
.
apply
(
this
,
arguments
);
};
});
// support for addIceCandidate(null or undefined)
var
nativeAddIceCandidate
=
RTCPeerConnection
.
prototype
.
addIceCandidate
;
RTCPeerConnection
.
prototype
.
addIceCandidate
=
function
()
{
if
(
!
arguments
[
0
])
{
if
(
arguments
[
1
])
{
arguments
[
1
].
apply
(
null
);
}
return
Promise
.
resolve
();
}
return
nativeAddIceCandidate
.
apply
(
this
,
arguments
);
};
}
};
// Expose public methods.
module
.
exports
=
{
shimMediaStream
:
chromeShim
.
shimMediaStream
,
shimOnTrack
:
chromeShim
.
shimOnTrack
,
shimSourceObject
:
chromeShim
.
shimSourceObject
,
shimPeerConnection
:
chromeShim
.
shimPeerConnection
,
shimGetUserMedia
:
require
(
'
./getusermedia
'
)
};
},{
"
../utils.js
"
:
10
,
"
./getusermedia
"
:
4
}],
4
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
var
logging
=
require
(
'
../utils.js
'
).
log
;
// Expose public methods.
module
.
exports
=
function
()
{
var
constraintsToChrome_
=
function
(
c
)
{
if
(
typeof
c
!==
'
object
'
||
c
.
mandatory
||
c
.
optional
)
{
return
c
;
}
var
cc
=
{};
Object
.
keys
(
c
).
forEach
(
function
(
key
)
{
if
(
key
===
'
require
'
||
key
===
'
advanced
'
||
key
===
'
mediaSource
'
)
{
return
;
}
var
r
=
(
typeof
c
[
key
]
===
'
object
'
)
?
c
[
key
]
:
{
ideal
:
c
[
key
]};
if
(
r
.
exact
!==
undefined
&&
typeof
r
.
exact
===
'
number
'
)
{
r
.
min
=
r
.
max
=
r
.
exact
;
}
var
oldname_
=
function
(
prefix
,
name
)
{
if
(
prefix
)
{
return
prefix
+
name
.
charAt
(
0
).
toUpperCase
()
+
name
.
slice
(
1
);
}
return
(
name
===
'
deviceId
'
)
?
'
sourceId
'
:
name
;
};
if
(
r
.
ideal
!==
undefined
)
{
cc
.
optional
=
cc
.
optional
||
[];
var
oc
=
{};
if
(
typeof
r
.
ideal
===
'
number
'
)
{
oc
[
oldname_
(
'
min
'
,
key
)]
=
r
.
ideal
;
cc
.
optional
.
push
(
oc
);
oc
=
{};
oc
[
oldname_
(
'
max
'
,
key
)]
=
r
.
ideal
;
cc
.
optional
.
push
(
oc
);
}
else
{
oc
[
oldname_
(
''
,
key
)]
=
r
.
ideal
;
cc
.
optional
.
push
(
oc
);
}
}
if
(
r
.
exact
!==
undefined
&&
typeof
r
.
exact
!==
'
number
'
)
{
cc
.
mandatory
=
cc
.
mandatory
||
{};
cc
.
mandatory
[
oldname_
(
''
,
key
)]
=
r
.
exact
;
}
else
{
[
'
min
'
,
'
max
'
].
forEach
(
function
(
mix
)
{
if
(
r
[
mix
]
!==
undefined
)
{
cc
.
mandatory
=
cc
.
mandatory
||
{};
cc
.
mandatory
[
oldname_
(
mix
,
key
)]
=
r
[
mix
];
}
});
}
});
if
(
c
.
advanced
)
{
cc
.
optional
=
(
cc
.
optional
||
[]).
concat
(
c
.
advanced
);
}
return
cc
;
};
var
shimConstraints_
=
function
(
constraints
,
func
)
{
constraints
=
JSON
.
parse
(
JSON
.
stringify
(
constraints
));
if
(
constraints
&&
constraints
.
audio
)
{
constraints
.
audio
=
constraintsToChrome_
(
constraints
.
audio
);
}
if
(
constraints
&&
typeof
constraints
.
video
===
'
object
'
)
{
// Shim facingMode for mobile, where it defaults to "user".
var
face
=
constraints
.
video
.
facingMode
;
face
=
face
&&
((
typeof
face
===
'
object
'
)
?
face
:
{
ideal
:
face
});
if
((
face
&&
(
face
.
exact
===
'
user
'
||
face
.
exact
===
'
environment
'
||
face
.
ideal
===
'
user
'
||
face
.
ideal
===
'
environment
'
))
&&
!
(
navigator
.
mediaDevices
.
getSupportedConstraints
&&
navigator
.
mediaDevices
.
getSupportedConstraints
().
facingMode
))
{
delete
constraints
.
video
.
facingMode
;
if
(
face
.
exact
===
'
environment
'
||
face
.
ideal
===
'
environment
'
)
{
// Look for "back" in label, or use last cam (typically back cam).
return
navigator
.
mediaDevices
.
enumerateDevices
()
.
then
(
function
(
devices
)
{
devices
=
devices
.
filter
(
function
(
d
)
{
return
d
.
kind
===
'
videoinput
'
;
});
var
back
=
devices
.
find
(
function
(
d
)
{
return
d
.
label
.
toLowerCase
().
indexOf
(
'
back
'
)
!==
-
1
;
})
||
(
devices
.
length
&&
devices
[
devices
.
length
-
1
]);
if
(
back
)
{
constraints
.
video
.
deviceId
=
face
.
exact
?
{
exact
:
back
.
deviceId
}
:
{
ideal
:
back
.
deviceId
};
}
constraints
.
video
=
constraintsToChrome_
(
constraints
.
video
);
logging
(
'
chrome:
'
+
JSON
.
stringify
(
constraints
));
return
func
(
constraints
);
});
}
}
constraints
.
video
=
constraintsToChrome_
(
constraints
.
video
);
}
logging
(
'
chrome:
'
+
JSON
.
stringify
(
constraints
));
return
func
(
constraints
);
};
var
shimError_
=
function
(
e
)
{
return
{
name
:
{
PermissionDeniedError
:
'
NotAllowedError
'
,
ConstraintNotSatisfiedError
:
'
OverconstrainedError
'
}[
e
.
name
]
||
e
.
name
,
message
:
e
.
message
,
constraint
:
e
.
constraintName
,
toString
:
function
()
{
return
this
.
name
+
(
this
.
message
&&
'
:
'
)
+
this
.
message
;
}
};
};
var
getUserMedia_
=
function
(
constraints
,
onSuccess
,
onError
)
{
shimConstraints_
(
constraints
,
function
(
c
)
{
navigator
.
webkitGetUserMedia
(
c
,
onSuccess
,
function
(
e
)
{
onError
(
shimError_
(
e
));
});
});
};
navigator
.
getUserMedia
=
getUserMedia_
;
// Returns the result of getUserMedia as a Promise.
var
getUserMediaPromise_
=
function
(
constraints
)
{
return
new
Promise
(
function
(
resolve
,
reject
)
{
navigator
.
getUserMedia
(
constraints
,
resolve
,
reject
);
});
};
if
(
!
navigator
.
mediaDevices
)
{
navigator
.
mediaDevices
=
{
getUserMedia
:
getUserMediaPromise_
,
enumerateDevices
:
function
()
{
return
new
Promise
(
function
(
resolve
)
{
var
kinds
=
{
audio
:
'
audioinput
'
,
video
:
'
videoinput
'
};
return
MediaStreamTrack
.
getSources
(
function
(
devices
)
{
resolve
(
devices
.
map
(
function
(
device
)
{
return
{
label
:
device
.
label
,
kind
:
kinds
[
device
.
kind
],
deviceId
:
device
.
id
,
groupId
:
''
};
}));
});
});
}
};
}
// A shim for getUserMedia method on the mediaDevices object.
// TODO(KaptenJansson) remove once implemented in Chrome stable.
if
(
!
navigator
.
mediaDevices
.
getUserMedia
)
{
navigator
.
mediaDevices
.
getUserMedia
=
function
(
constraints
)
{
return
getUserMediaPromise_
(
constraints
);
};
}
else
{
// Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
// function which returns a Promise, it does not accept spec-style
// constraints.
var
origGetUserMedia
=
navigator
.
mediaDevices
.
getUserMedia
.
bind
(
navigator
.
mediaDevices
);
navigator
.
mediaDevices
.
getUserMedia
=
function
(
cs
)
{
return
shimConstraints_
(
cs
,
function
(
c
)
{
return
origGetUserMedia
(
c
).
then
(
function
(
stream
)
{
if
(
c
.
audio
&&
!
stream
.
getAudioTracks
().
length
||
c
.
video
&&
!
stream
.
getVideoTracks
().
length
)
{
stream
.
getTracks
().
forEach
(
function
(
track
)
{
track
.
stop
();
});
throw
new
DOMException
(
''
,
'
NotFoundError
'
);
}
return
stream
;
},
function
(
e
)
{
return
Promise
.
reject
(
shimError_
(
e
));
});
});
};
}
// Dummy devicechange event methods.
// TODO(KaptenJansson) remove once implemented in Chrome stable.
if
(
typeof
navigator
.
mediaDevices
.
addEventListener
===
'
undefined
'
)
{
navigator
.
mediaDevices
.
addEventListener
=
function
()
{
logging
(
'
Dummy mediaDevices.addEventListener called.
'
);
};
}
if
(
typeof
navigator
.
mediaDevices
.
removeEventListener
===
'
undefined
'
)
{
navigator
.
mediaDevices
.
removeEventListener
=
function
()
{
logging
(
'
Dummy mediaDevices.removeEventListener called.
'
);
};
}
};
},{
"
../utils.js
"
:
10
}],
5
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
var
SDPUtils
=
require
(
'
sdp
'
);
var
browserDetails
=
require
(
'
../utils
'
).
browserDetails
;
var
edgeShim
=
{
shimPeerConnection
:
function
()
{
if
(
window
.
RTCIceGatherer
)
{
// ORTC defines an RTCIceCandidate object but no constructor.
// Not implemented in Edge.
if
(
!
window
.
RTCIceCandidate
)
{
window
.
RTCIceCandidate
=
function
(
args
)
{
return
args
;
};
}
// ORTC does not have a session description object but
// other browsers (i.e. Chrome) that will support both PC and ORTC
// in the future might have this defined already.
if
(
!
window
.
RTCSessionDescription
)
{
window
.
RTCSessionDescription
=
function
(
args
)
{
return
args
;
};
}
// this adds an additional event listener to MediaStrackTrack that signals
// when a tracks enabled property was changed.
var
origMSTEnabled
=
Object
.
getOwnPropertyDescriptor
(
MediaStreamTrack
.
prototype
,
'
enabled
'
);
Object
.
defineProperty
(
MediaStreamTrack
.
prototype
,
'
enabled
'
,
{
set
:
function
(
value
)
{
origMSTEnabled
.
set
.
call
(
this
,
value
);
var
ev
=
new
Event
(
'
enabled
'
);
ev
.
enabled
=
value
;
this
.
dispatchEvent
(
ev
);
}
});
}
window
.
RTCPeerConnection
=
function
(
config
)
{
var
self
=
this
;
var
_eventTarget
=
document
.
createDocumentFragment
();
[
'
addEventListener
'
,
'
removeEventListener
'
,
'
dispatchEvent
'
]
.
forEach
(
function
(
method
)
{
self
[
method
]
=
_eventTarget
[
method
].
bind
(
_eventTarget
);
});
this
.
onicecandidate
=
null
;
this
.
onaddstream
=
null
;
this
.
ontrack
=
null
;
this
.
onremovestream
=
null
;
this
.
onsignalingstatechange
=
null
;
this
.
oniceconnectionstatechange
=
null
;
this
.
onnegotiationneeded
=
null
;
this
.
ondatachannel
=
null
;
this
.
localStreams
=
[];
this
.
remoteStreams
=
[];
this
.
getLocalStreams
=
function
()
{
return
self
.
localStreams
;
};
this
.
getRemoteStreams
=
function
()
{
return
self
.
remoteStreams
;
};
this
.
localDescription
=
new
RTCSessionDescription
({
type
:
''
,
sdp
:
''
});
this
.
remoteDescription
=
new
RTCSessionDescription
({
type
:
''
,
sdp
:
''
});
this
.
signalingState
=
'
stable
'
;
this
.
iceConnectionState
=
'
new
'
;
this
.
iceGatheringState
=
'
new
'
;
this
.
iceOptions
=
{
gatherPolicy
:
'
all
'
,
iceServers
:
[]
};
if
(
config
&&
config
.
iceTransportPolicy
)
{
switch
(
config
.
iceTransportPolicy
)
{
case
'
all
'
:
case
'
relay
'
:
this
.
iceOptions
.
gatherPolicy
=
config
.
iceTransportPolicy
;
break
;
case
'
none
'
:
// FIXME: remove once implementation and spec have added this.
throw
new
TypeError
(
'
iceTransportPolicy "none" not supported
'
);
default
:
// don't set iceTransportPolicy.
break
;
}
}
this
.
usingBundle
=
config
&&
config
.
bundlePolicy
===
'
max-bundle
'
;
if
(
config
&&
config
.
iceServers
)
{
// Edge does not like
// 1) stun:
// 2) turn: that does not have all of turn:host:port?transport=udp
// 3) turn: with ipv6 addresses
var
iceServers
=
JSON
.
parse
(
JSON
.
stringify
(
config
.
iceServers
));
this
.
iceOptions
.
iceServers
=
iceServers
.
filter
(
function
(
server
)
{
if
(
server
&&
server
.
urls
)
{
var
urls
=
server
.
urls
;
if
(
typeof
urls
===
'
string
'
)
{
urls
=
[
urls
];
}
urls
=
urls
.
filter
(
function
(
url
)
{
return
(
url
.
indexOf
(
'
turn:
'
)
===
0
&&
url
.
indexOf
(
'
transport=udp
'
)
!==
-
1
&&
url
.
indexOf
(
'
turn:[
'
)
===
-
1
)
||
(
url
.
indexOf
(
'
stun:
'
)
===
0
&&
browserDetails
.
version
>=
14393
);
})[
0
];
return
!!
urls
;
}
return
false
;
});
}
this
.
_config
=
config
;
// per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
// everything that is needed to describe a SDP m-line.
this
.
transceivers
=
[];
// since the iceGatherer is currently created in createOffer but we
// must not emit candidates until after setLocalDescription we buffer
// them in this array.
this
.
_localIceCandidatesBuffer
=
[];
};
window
.
RTCPeerConnection
.
prototype
.
_emitBufferedCandidates
=
function
()
{
var
self
=
this
;
var
sections
=
SDPUtils
.
splitSections
(
self
.
localDescription
.
sdp
);
// FIXME: need to apply ice candidates in a way which is async but
// in-order
this
.
_localIceCandidatesBuffer
.
forEach
(
function
(
event
)
{
var
end
=
!
event
.
candidate
||
Object
.
keys
(
event
.
candidate
).
length
===
0
;
if
(
end
)
{
for
(
var
j
=
1
;
j
<
sections
.
length
;
j
++
)
{
if
(
sections
[
j
].
indexOf
(
'
\r\n
a=end-of-candidates
\r\n
'
)
===
-
1
)
{
sections
[
j
]
+=
'
a=end-of-candidates
\r\n
'
;
}
}
}
else
{
sections
[
event
.
candidate
.
sdpMLineIndex
+
1
]
+=
'
a=
'
+
event
.
candidate
.
candidate
+
'
\r\n
'
;
}
self
.
localDescription
.
sdp
=
sections
.
join
(
''
);
self
.
dispatchEvent
(
event
);
if
(
self
.
onicecandidate
!==
null
)
{
self
.
onicecandidate
(
event
);
}
if
(
!
event
.
candidate
&&
self
.
iceGatheringState
!==
'
complete
'
)
{
var
complete
=
self
.
transceivers
.
every
(
function
(
transceiver
)
{
return
transceiver
.
iceGatherer
&&
transceiver
.
iceGatherer
.
state
===
'
completed
'
;
});
if
(
complete
)
{
self
.
iceGatheringState
=
'
complete
'
;
}
}
});
this
.
_localIceCandidatesBuffer
=
[];
};
window
.
RTCPeerConnection
.
prototype
.
getConfiguration
=
function
()
{
return
this
.
_config
;
};
window
.
RTCPeerConnection
.
prototype
.
addStream
=
function
(
stream
)
{
// Clone is necessary for local demos mostly, attaching directly
// to two different senders does not work (build 10547).
var
clonedStream
=
stream
.
clone
();
stream
.
getTracks
().
forEach
(
function
(
track
,
idx
)
{
var
clonedTrack
=
clonedStream
.
getTracks
()[
idx
];
track
.
addEventListener
(
'
enabled
'
,
function
(
event
)
{
clonedTrack
.
enabled
=
event
.
enabled
;
});
});
this
.
localStreams
.
push
(
clonedStream
);
this
.
_maybeFireNegotiationNeeded
();
};
window
.
RTCPeerConnection
.
prototype
.
removeStream
=
function
(
stream
)
{
var
idx
=
this
.
localStreams
.
indexOf
(
stream
);
if
(
idx
>
-
1
)
{
this
.
localStreams
.
splice
(
idx
,
1
);
this
.
_maybeFireNegotiationNeeded
();
}
};
window
.
RTCPeerConnection
.
prototype
.
getSenders
=
function
()
{
return
this
.
transceivers
.
filter
(
function
(
transceiver
)
{
return
!!
transceiver
.
rtpSender
;
})
.
map
(
function
(
transceiver
)
{
return
transceiver
.
rtpSender
;
});
};
window
.
RTCPeerConnection
.
prototype
.
getReceivers
=
function
()
{
return
this
.
transceivers
.
filter
(
function
(
transceiver
)
{
return
!!
transceiver
.
rtpReceiver
;
})
.
map
(
function
(
transceiver
)
{
return
transceiver
.
rtpReceiver
;
});
};
// Determines the intersection of local and remote capabilities.
window
.
RTCPeerConnection
.
prototype
.
_getCommonCapabilities
=
function
(
localCapabilities
,
remoteCapabilities
)
{
var
commonCapabilities
=
{
codecs
:
[],
headerExtensions
:
[],
fecMechanisms
:
[]
};
localCapabilities
.
codecs
.
forEach
(
function
(
lCodec
)
{
for
(
var
i
=
0
;
i
<
remoteCapabilities
.
codecs
.
length
;
i
++
)
{
var
rCodec
=
remoteCapabilities
.
codecs
[
i
];
if
(
lCodec
.
name
.
toLowerCase
()
===
rCodec
.
name
.
toLowerCase
()
&&
lCodec
.
clockRate
===
rCodec
.
clockRate
)
{
// number of channels is the highest common number of channels
rCodec
.
numChannels
=
Math
.
min
(
lCodec
.
numChannels
,
rCodec
.
numChannels
);
// push rCodec so we reply with offerer payload type
commonCapabilities
.
codecs
.
push
(
rCodec
);
// determine common feedback mechanisms
rCodec
.
rtcpFeedback
=
rCodec
.
rtcpFeedback
.
filter
(
function
(
fb
)
{
for
(
var
j
=
0
;
j
<
lCodec
.
rtcpFeedback
.
length
;
j
++
)
{
if
(
lCodec
.
rtcpFeedback
[
j
].
type
===
fb
.
type
&&
lCodec
.
rtcpFeedback
[
j
].
parameter
===
fb
.
parameter
)
{
return
true
;
}
}
return
false
;
});
// FIXME: also need to determine .parameters
// see https://github.com/openpeer/ortc/issues/569
break
;
}
}
});
localCapabilities
.
headerExtensions
.
forEach
(
function
(
lHeaderExtension
)
{
for
(
var
i
=
0
;
i
<
remoteCapabilities
.
headerExtensions
.
length
;
i
++
)
{
var
rHeaderExtension
=
remoteCapabilities
.
headerExtensions
[
i
];
if
(
lHeaderExtension
.
uri
===
rHeaderExtension
.
uri
)
{
commonCapabilities
.
headerExtensions
.
push
(
rHeaderExtension
);
break
;
}
}
});
// FIXME: fecMechanisms
return
commonCapabilities
;
};
// Create ICE gatherer, ICE transport and DTLS transport.
window
.
RTCPeerConnection
.
prototype
.
_createIceAndDtlsTransports
=
function
(
mid
,
sdpMLineIndex
)
{
var
self
=
this
;
var
iceGatherer
=
new
RTCIceGatherer
(
self
.
iceOptions
);
var
iceTransport
=
new
RTCIceTransport
(
iceGatherer
);
iceGatherer
.
onlocalcandidate
=
function
(
evt
)
{
var
event
=
new
Event
(
'
icecandidate
'
);
event
.
candidate
=
{
sdpMid
:
mid
,
sdpMLineIndex
:
sdpMLineIndex
};
var
cand
=
evt
.
candidate
;
var
end
=
!
cand
||
Object
.
keys
(
cand
).
length
===
0
;
// Edge emits an empty object for RTCIceCandidateComplete‥
if
(
end
)
{
// polyfill since RTCIceGatherer.state is not implemented in
// Edge 10547 yet.
if
(
iceGatherer
.
state
===
undefined
)
{
iceGatherer
.
state
=
'
completed
'
;
}
}
else
{
// RTCIceCandidate doesn't have a component, needs to be added
cand
.
component
=
iceTransport
.
component
===
'
RTCP
'
?
2
:
1
;
event
.
candidate
.
candidate
=
SDPUtils
.
writeCandidate
(
cand
);
}
// update local description.
var
sections
=
SDPUtils
.
splitSections
(
self
.
localDescription
.
sdp
);
if
(
!
end
)
{
sections
[
event
.
candidate
.
sdpMLineIndex
+
1
]
+=
'
a=
'
+
event
.
candidate
.
candidate
+
'
\r\n
'
;
}
else
{
sections
[
event
.
candidate
.
sdpMLineIndex
+
1
]
+=
'
a=end-of-candidates
\r\n
'
;
}
self
.
localDescription
.
sdp
=
sections
.
join
(
''
);
var
transceivers
=
self
.
_pendingOffer
?
self
.
_pendingOffer
:
self
.
transceivers
;
var
complete
=
transceivers
.
every
(
function
(
transceiver
)
{
return
transceiver
.
iceGatherer
&&
transceiver
.
iceGatherer
.
state
===
'
completed
'
;
});
// Emit candidate if localDescription is set.
// Also emits null candidate when all gatherers are complete.
switch
(
self
.
iceGatheringState
)
{
case
'
new
'
:
if
(
!
end
)
{
self
.
_localIceCandidatesBuffer
.
push
(
event
);
}
if
(
end
&&
complete
)
{
self
.
_localIceCandidatesBuffer
.
push
(
new
Event
(
'
icecandidate
'
));
}
break
;
case
'
gathering
'
:
self
.
_emitBufferedCandidates
();
if
(
!
end
)
{
self
.
dispatchEvent
(
event
);
if
(
self
.
onicecandidate
!==
null
)
{
self
.
onicecandidate
(
event
);
}
}
if
(
complete
)
{
self
.
dispatchEvent
(
new
Event
(
'
icecandidate
'
));
if
(
self
.
onicecandidate
!==
null
)
{
self
.
onicecandidate
(
new
Event
(
'
icecandidate
'
));
}
self
.
iceGatheringState
=
'
complete
'
;
}
break
;
case
'
complete
'
:
// should not happen... currently!
break
;
default
:
// no-op.
break
;
}
};
iceTransport
.
onicestatechange
=
function
()
{
self
.
_updateConnectionState
();
};
var
dtlsTransport
=
new
RTCDtlsTransport
(
iceTransport
);
dtlsTransport
.
ondtlsstatechange
=
function
()
{
self
.
_updateConnectionState
();
};
dtlsTransport
.
onerror
=
function
()
{
// onerror does not set state to failed by itself.
dtlsTransport
.
state
=
'
failed
'
;
self
.
_updateConnectionState
();
};
return
{
iceGatherer
:
iceGatherer
,
iceTransport
:
iceTransport
,
dtlsTransport
:
dtlsTransport
};
};
// Start the RTP Sender and Receiver for a transceiver.
window
.
RTCPeerConnection
.
prototype
.
_transceive
=
function
(
transceiver
,
send
,
recv
)
{
var
params
=
this
.
_getCommonCapabilities
(
transceiver
.
localCapabilities
,
transceiver
.
remoteCapabilities
);
if
(
send
&&
transceiver
.
rtpSender
)
{
params
.
encodings
=
transceiver
.
sendEncodingParameters
;
params
.
rtcp
=
{
cname
:
SDPUtils
.
localCName
};
if
(
transceiver
.
recvEncodingParameters
.
length
)
{
params
.
rtcp
.
ssrc
=
transceiver
.
recvEncodingParameters
[
0
].
ssrc
;
}
transceiver
.
rtpSender
.
send
(
params
);
}
if
(
recv
&&
transceiver
.
rtpReceiver
)
{
// remove RTX field in Edge 14942
if
(
transceiver
.
kind
===
'
video
'
&&
transceiver
.
recvEncodingParameters
)
{
transceiver
.
recvEncodingParameters
.
forEach
(
function
(
p
)
{
delete
p
.
rtx
;
});
}
params
.
encodings
=
transceiver
.
recvEncodingParameters
;
params
.
rtcp
=
{
cname
:
transceiver
.
cname
};
if
(
transceiver
.
sendEncodingParameters
.
length
)
{
params
.
rtcp
.
ssrc
=
transceiver
.
sendEncodingParameters
[
0
].
ssrc
;
}
transceiver
.
rtpReceiver
.
receive
(
params
);
}
};
window
.
RTCPeerConnection
.
prototype
.
setLocalDescription
=
function
(
description
)
{
var
self
=
this
;
var
sections
;
var
sessionpart
;
if
(
description
.
type
===
'
offer
'
)
{
// FIXME: What was the purpose of this empty if statement?
// if (!this._pendingOffer) {
// } else {
if
(
this
.
_pendingOffer
)
{
// VERY limited support for SDP munging. Limited to:
// * changing the order of codecs
sections
=
SDPUtils
.
splitSections
(
description
.
sdp
);
sessionpart
=
sections
.
shift
();
sections
.
forEach
(
function
(
mediaSection
,
sdpMLineIndex
)
{
var
caps
=
SDPUtils
.
parseRtpParameters
(
mediaSection
);
self
.
_pendingOffer
[
sdpMLineIndex
].
localCapabilities
=
caps
;
});
this
.
transceivers
=
this
.
_pendingOffer
;
delete
this
.
_pendingOffer
;
}
}
else
if
(
description
.
type
===
'
answer
'
)
{
sections
=
SDPUtils
.
splitSections
(
self
.
remoteDescription
.
sdp
);
sessionpart
=
sections
.
shift
();
var
isIceLite
=
SDPUtils
.
matchPrefix
(
sessionpart
,
'
a=ice-lite
'
).
length
>
0
;
sections
.
forEach
(
function
(
mediaSection
,
sdpMLineIndex
)
{
var
transceiver
=
self
.
transceivers
[
sdpMLineIndex
];
var
iceGatherer
=
transceiver
.
iceGatherer
;
var
iceTransport
=
transceiver
.
iceTransport
;
var
dtlsTransport
=
transceiver
.
dtlsTransport
;
var
localCapabilities
=
transceiver
.
localCapabilities
;
var
remoteCapabilities
=
transceiver
.
remoteCapabilities
;
var
rejected
=
mediaSection
.
split
(
'
\n
'
,
1
)[
0
]
.
split
(
'
'
,
2
)[
1
]
===
'
0
'
;
if
(
!
rejected
&&
!
transceiver
.
isDatachannel
)
{
var
remoteIceParameters
=
SDPUtils
.
getIceParameters
(
mediaSection
,
sessionpart
);
var
remoteDtlsParameters
=
SDPUtils
.
getDtlsParameters
(
mediaSection
,
sessionpart
);
if
(
isIceLite
)
{
remoteDtlsParameters
.
role
=
'
server
'
;
}
if
(
!
self
.
usingBundle
||
sdpMLineIndex
===
0
)
{
iceTransport
.
start
(
iceGatherer
,
remoteIceParameters
,
isIceLite
?
'
controlling
'
:
'
controlled
'
);
dtlsTransport
.
start
(
remoteDtlsParameters
);
}
// Calculate intersection of capabilities.
var
params
=
self
.
_getCommonCapabilities
(
localCapabilities
,
remoteCapabilities
);
// Start the RTCRtpSender. The RTCRtpReceiver for this
// transceiver has already been started in setRemoteDescription.
self
.
_transceive
(
transceiver
,
params
.
codecs
.
length
>
0
,
false
);
}
});
}
this
.
localDescription
=
{
type
:
description
.
type
,
sdp
:
description
.
sdp
};
switch
(
description
.
type
)
{
case
'
offer
'
:
this
.
_updateSignalingState
(
'
have-local-offer
'
);
break
;
case
'
answer
'
:
this
.
_updateSignalingState
(
'
stable
'
);
break
;
default
:
throw
new
TypeError
(
'
unsupported type "
'
+
description
.
type
+
'
"
'
);
}
// If a success callback was provided, emit ICE candidates after it
// has been executed. Otherwise, emit callback after the Promise is
// resolved.
var
hasCallback
=
arguments
.
length
>
1
&&
typeof
arguments
[
1
]
===
'
function
'
;
if
(
hasCallback
)
{
var
cb
=
arguments
[
1
];
window
.
setTimeout
(
function
()
{
cb
();
if
(
self
.
iceGatheringState
===
'
new
'
)
{
self
.
iceGatheringState
=
'
gathering
'
;
}
self
.
_emitBufferedCandidates
();
},
0
);
}
var
p
=
Promise
.
resolve
();
p
.
then
(
function
()
{
if
(
!
hasCallback
)
{
if
(
self
.
iceGatheringState
===
'
new
'
)
{
self
.
iceGatheringState
=
'
gathering
'
;
}
// Usually candidates will be emitted earlier.
window
.
setTimeout
(
self
.
_emitBufferedCandidates
.
bind
(
self
),
500
);
}
});
return
p
;
};
window
.
RTCPeerConnection
.
prototype
.
setRemoteDescription
=
function
(
description
)
{
var
self
=
this
;
var
stream
=
new
MediaStream
();
var
receiverList
=
[];
var
sections
=
SDPUtils
.
splitSections
(
description
.
sdp
);
var
sessionpart
=
sections
.
shift
();
var
isIceLite
=
SDPUtils
.
matchPrefix
(
sessionpart
,
'
a=ice-lite
'
).
length
>
0
;
this
.
usingBundle
=
SDPUtils
.
matchPrefix
(
sessionpart
,
'
a=group:BUNDLE
'
).
length
>
0
;
sections
.
forEach
(
function
(
mediaSection
,
sdpMLineIndex
)
{
var
lines
=
SDPUtils
.
splitLines
(
mediaSection
);
var
mline
=
lines
[
0
].
substr
(
2
).
split
(
'
'
);
var
kind
=
mline
[
0
];
var
rejected
=
mline
[
1
]
===
'
0
'
;
var
direction
=
SDPUtils
.
getDirection
(
mediaSection
,
sessionpart
);
var
mid
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=mid:
'
);
if
(
mid
.
length
)
{
mid
=
mid
[
0
].
substr
(
6
);
}
else
{
mid
=
SDPUtils
.
generateIdentifier
();
}
// Reject datachannels which are not implemented yet.
if
(
kind
===
'
application
'
&&
mline
[
2
]
===
'
DTLS/SCTP
'
)
{
self
.
transceivers
[
sdpMLineIndex
]
=
{
mid
:
mid
,
isDatachannel
:
true
};
return
;
}
var
transceiver
;
var
iceGatherer
;
var
iceTransport
;
var
dtlsTransport
;
var
rtpSender
;
var
rtpReceiver
;
var
sendEncodingParameters
;
var
recvEncodingParameters
;
var
localCapabilities
;
var
track
;
// FIXME: ensure the mediaSection has rtcp-mux set.
var
remoteCapabilities
=
SDPUtils
.
parseRtpParameters
(
mediaSection
);
var
remoteIceParameters
;
var
remoteDtlsParameters
;
if
(
!
rejected
)
{
remoteIceParameters
=
SDPUtils
.
getIceParameters
(
mediaSection
,
sessionpart
);
remoteDtlsParameters
=
SDPUtils
.
getDtlsParameters
(
mediaSection
,
sessionpart
);
remoteDtlsParameters
.
role
=
'
client
'
;
}
recvEncodingParameters
=
SDPUtils
.
parseRtpEncodingParameters
(
mediaSection
);
var
cname
;
// Gets the first SSRC. Note that with RTX there might be multiple
// SSRCs.
var
remoteSsrc
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=ssrc:
'
)
.
map
(
function
(
line
)
{
return
SDPUtils
.
parseSsrcMedia
(
line
);
})
.
filter
(
function
(
obj
)
{
return
obj
.
attribute
===
'
cname
'
;
})[
0
];
if
(
remoteSsrc
)
{
cname
=
remoteSsrc
.
value
;
}
var
isComplete
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=end-of-candidates
'
,
sessionpart
).
length
>
0
;
var
cands
=
SDPUtils
.
matchPrefix
(
mediaSection
,
'
a=candidate:
'
)
.
map
(
function
(
cand
)
{
return
SDPUtils
.
parseCandidate
(
cand
);
})
.
filter
(
function
(
cand
)
{
return
cand
.
component
===
'
1
'
;
});
if
(
description
.
type
===
'
offer
'
&&
!
rejected
)
{
var
transports
=
self
.
usingBundle
&&
sdpMLineIndex
>
0
?
{
iceGatherer
:
self
.
transceivers
[
0
].
iceGatherer
,
iceTransport
:
self
.
transceivers
[
0
].
iceTransport
,
dtlsTransport
:
self
.
transceivers
[
0
].
dtlsTransport
}
:
self
.
_createIceAndDtlsTransports
(
mid
,
sdpMLineIndex
);
if
(
isComplete
&&
(
!
self
.
usingBundle
||
sdpMLineIndex
===
0
))
{
transports
.
iceTransport
.
setRemoteCandidates
(
cands
);
}
localCapabilities
=
RTCRtpReceiver
.
getCapabilities
(
kind
);
// filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
localCapabilities
.
codecs
=
localCapabilities
.
codecs
.
filter
(
function
(
codec
)
{
return
codec
.
name
!==
'
rtx
'
;
});
sendEncodingParameters
=
[{
ssrc
:
(
2
*
sdpMLineIndex
+
2
)
*
1001
}];
rtpReceiver
=
new
RTCRtpReceiver
(
transports
.
dtlsTransport
,
kind
);
track
=
rtpReceiver
.
track
;
receiverList
.
push
([
track
,
rtpReceiver
]);
// FIXME: not correct when there are multiple streams but that is
// not currently supported in this shim.
stream
.
addTrack
(
track
);
// FIXME: look at direction.
if
(
self
.
localStreams
.
length
>
0
&&
self
.
localStreams
[
0
].
getTracks
().
length
>=
sdpMLineIndex
)
{
var
localTrack
;
if
(
kind
===
'
audio
'
)
{
localTrack
=
self
.
localStreams
[
0
].
getAudioTracks
()[
0
];
}
else
if
(
kind
===
'
video
'
)
{
localTrack
=
self
.
localStreams
[
0
].
getVideoTracks
()[
0
];
}
if
(
localTrack
)
{
rtpSender
=
new
RTCRtpSender
(
localTrack
,
transports
.
dtlsTransport
);
}
}
self
.
transceivers
[
sdpMLineIndex
]
=
{
iceGatherer
:
transports
.
iceGatherer
,
iceTransport
:
transports
.
iceTransport
,
dtlsTransport
:
transports
.
dtlsTransport
,
localCapabilities
:
localCapabilities
,
remoteCapabilities
:
remoteCapabilities
,
rtpSender
:
rtpSender
,
rtpReceiver
:
rtpReceiver
,
kind
:
kind
,
mid
:
mid
,
cname
:
cname
,
sendEncodingParameters
:
sendEncodingParameters
,
recvEncodingParameters
:
recvEncodingParameters
};
// Start the RTCRtpReceiver now. The RTPSender is started in
// setLocalDescription.
self
.
_transceive
(
self
.
transceivers
[
sdpMLineIndex
],
false
,
direction
===
'
sendrecv
'
||
direction
===
'
sendonly
'
);
}
else
if
(
description
.
type
===
'
answer
'
&&
!
rejected
)
{
transceiver
=
self
.
transceivers
[
sdpMLineIndex
];
iceGatherer
=
transceiver
.
iceGatherer
;
iceTransport
=
transceiver
.
iceTransport
;
dtlsTransport
=
transceiver
.
dtlsTransport
;
rtpSender
=
transceiver
.
rtpSender
;
rtpReceiver
=
transceiver
.
rtpReceiver
;
sendEncodingParameters
=
transceiver
.
sendEncodingParameters
;
localCapabilities
=
transceiver
.
localCapabilities
;
self
.
transceivers
[
sdpMLineIndex
].
recvEncodingParameters
=
recvEncodingParameters
;
self
.
transceivers
[
sdpMLineIndex
].
remoteCapabilities
=
remoteCapabilities
;
self
.
transceivers
[
sdpMLineIndex
].
cname
=
cname
;
if
((
isIceLite
||
isComplete
)
&&
cands
.
length
)
{
iceTransport
.
setRemoteCandidates
(
cands
);
}
if
(
!
self
.
usingBundle
||
sdpMLineIndex
===
0
)
{
iceTransport
.
start
(
iceGatherer
,
remoteIceParameters
,
'
controlling
'
);
dtlsTransport
.
start
(
remoteDtlsParameters
);
}
self
.
_transceive
(
transceiver
,
direction
===
'
sendrecv
'
||
direction
===
'
recvonly
'
,
direction
===
'
sendrecv
'
||
direction
===
'
sendonly
'
);
if
(
rtpReceiver
&&
(
direction
===
'
sendrecv
'
||
direction
===
'
sendonly
'
))
{
track
=
rtpReceiver
.
track
;
receiverList
.
push
([
track
,
rtpReceiver
]);
stream
.
addTrack
(
track
);
}
else
{
// FIXME: actually the receiver should be created later.
delete
transceiver
.
rtpReceiver
;
}
}
});
this
.
remoteDescription
=
{
type
:
description
.
type
,
sdp
:
description
.
sdp
};
switch
(
description
.
type
)
{
case
'
offer
'
:
this
.
_updateSignalingState
(
'
have-remote-offer
'
);
break
;
case
'
answer
'
:
this
.
_updateSignalingState
(
'
stable
'
);
break
;
default
:
throw
new
TypeError
(
'
unsupported type "
'
+
description
.
type
+
'
"
'
);
}
if
(
stream
.
getTracks
().
length
)
{
self
.
remoteStreams
.
push
(
stream
);
window
.
setTimeout
(
function
()
{
var
event
=
new
Event
(
'
addstream
'
);
event
.
stream
=
stream
;
self
.
dispatchEvent
(
event
);
if
(
self
.
onaddstream
!==
null
)
{
window
.
setTimeout
(
function
()
{
self
.
onaddstream
(
event
);
},
0
);
}
receiverList
.
forEach
(
function
(
item
)
{
var
track
=
item
[
0
];
var
receiver
=
item
[
1
];
var
trackEvent
=
new
Event
(
'
track
'
);
trackEvent
.
track
=
track
;
trackEvent
.
receiver
=
receiver
;
trackEvent
.
streams
=
[
stream
];
self
.
dispatchEvent
(
trackEvent
);
if
(
self
.
ontrack
!==
null
)
{
window
.
setTimeout
(
function
()
{
self
.
ontrack
(
trackEvent
);
},
0
);
}
});
},
0
);
}
if
(
arguments
.
length
>
1
&&
typeof
arguments
[
1
]
===
'
function
'
)
{
window
.
setTimeout
(
arguments
[
1
],
0
);
}
return
Promise
.
resolve
();
};
window
.
RTCPeerConnection
.
prototype
.
close
=
function
()
{
this
.
transceivers
.
forEach
(
function
(
transceiver
)
{
/* not yet
if (transceiver.iceGatherer) {
transceiver.iceGatherer.close();
}
*/
if
(
transceiver
.
iceTransport
)
{
transceiver
.
iceTransport
.
stop
();
}
if
(
transceiver
.
dtlsTransport
)
{
transceiver
.
dtlsTransport
.
stop
();
}
if
(
transceiver
.
rtpSender
)
{
transceiver
.
rtpSender
.
stop
();
}
if
(
transceiver
.
rtpReceiver
)
{
transceiver
.
rtpReceiver
.
stop
();
}
});
// FIXME: clean up tracks, local streams, remote streams, etc
this
.
_updateSignalingState
(
'
closed
'
);
};
// Update the signaling state.
window
.
RTCPeerConnection
.
prototype
.
_updateSignalingState
=
function
(
newState
)
{
this
.
signalingState
=
newState
;
var
event
=
new
Event
(
'
signalingstatechange
'
);
this
.
dispatchEvent
(
event
);
if
(
this
.
onsignalingstatechange
!==
null
)
{
this
.
onsignalingstatechange
(
event
);
}
};
// Determine whether to fire the negotiationneeded event.
window
.
RTCPeerConnection
.
prototype
.
_maybeFireNegotiationNeeded
=
function
()
{
// Fire away (for now).
var
event
=
new
Event
(
'
negotiationneeded
'
);
this
.
dispatchEvent
(
event
);
if
(
this
.
onnegotiationneeded
!==
null
)
{
this
.
onnegotiationneeded
(
event
);
}
};
// Update the connection state.
window
.
RTCPeerConnection
.
prototype
.
_updateConnectionState
=
function
()
{
var
self
=
this
;
var
newState
;
var
states
=
{
'
new
'
:
0
,
closed
:
0
,
connecting
:
0
,
checking
:
0
,
connected
:
0
,
completed
:
0
,
failed
:
0
};
this
.
transceivers
.
forEach
(
function
(
transceiver
)
{
states
[
transceiver
.
iceTransport
.
state
]
++
;
states
[
transceiver
.
dtlsTransport
.
state
]
++
;
});
// ICETransport.completed and connected are the same for this purpose.
states
.
connected
+=
states
.
completed
;
newState
=
'
new
'
;
if
(
states
.
failed
>
0
)
{
newState
=
'
failed
'
;
}
else
if
(
states
.
connecting
>
0
||
states
.
checking
>
0
)
{
newState
=
'
connecting
'
;
}
else
if
(
states
.
disconnected
>
0
)
{
newState
=
'
disconnected
'
;
}
else
if
(
states
.
new
>
0
)
{
newState
=
'
new
'
;
}
else
if
(
states
.
connected
>
0
||
states
.
completed
>
0
)
{
newState
=
'
connected
'
;
}
if
(
newState
!==
self
.
iceConnectionState
)
{
self
.
iceConnectionState
=
newState
;
var
event
=
new
Event
(
'
iceconnectionstatechange
'
);
this
.
dispatchEvent
(
event
);
if
(
this
.
oniceconnectionstatechange
!==
null
)
{
this
.
oniceconnectionstatechange
(
event
);
}
}
};
window
.
RTCPeerConnection
.
prototype
.
createOffer
=
function
()
{
var
self
=
this
;
if
(
this
.
_pendingOffer
)
{
throw
new
Error
(
'
createOffer called while there is a pending offer.
'
);
}
var
offerOptions
;
if
(
arguments
.
length
===
1
&&
typeof
arguments
[
0
]
!==
'
function
'
)
{
offerOptions
=
arguments
[
0
];
}
else
if
(
arguments
.
length
===
3
)
{
offerOptions
=
arguments
[
2
];
}
var
tracks
=
[];
var
numAudioTracks
=
0
;
var
numVideoTracks
=
0
;
// Default to sendrecv.
if
(
this
.
localStreams
.
length
)
{
numAudioTracks
=
this
.
localStreams
[
0
].
getAudioTracks
().
length
;
numVideoTracks
=
this
.
localStreams
[
0
].
getVideoTracks
().
length
;
}
// Determine number of audio and video tracks we need to send/recv.
if
(
offerOptions
)
{
// Reject Chrome legacy constraints.
if
(
offerOptions
.
mandatory
||
offerOptions
.
optional
)
{
throw
new
TypeError
(
'
Legacy mandatory/optional constraints not supported.
'
);
}
if
(
offerOptions
.
offerToReceiveAudio
!==
undefined
)
{
numAudioTracks
=
offerOptions
.
offerToReceiveAudio
;
}
if
(
offerOptions
.
offerToReceiveVideo
!==
undefined
)
{
numVideoTracks
=
offerOptions
.
offerToReceiveVideo
;
}
}
if
(
this
.
localStreams
.
length
)
{
// Push local streams.
this
.
localStreams
[
0
].
getTracks
().
forEach
(
function
(
track
)
{
tracks
.
push
({
kind
:
track
.
kind
,
track
:
track
,
wantReceive
:
track
.
kind
===
'
audio
'
?
numAudioTracks
>
0
:
numVideoTracks
>
0
});
if
(
track
.
kind
===
'
audio
'
)
{
numAudioTracks
--
;
}
else
if
(
track
.
kind
===
'
video
'
)
{
numVideoTracks
--
;
}
});
}
// Create M-lines for recvonly streams.
while
(
numAudioTracks
>
0
||
numVideoTracks
>
0
)
{
if
(
numAudioTracks
>
0
)
{
tracks
.
push
({
kind
:
'
audio
'
,
wantReceive
:
true
});
numAudioTracks
--
;
}
if
(
numVideoTracks
>
0
)
{
tracks
.
push
({
kind
:
'
video
'
,
wantReceive
:
true
});
numVideoTracks
--
;
}
}
var
sdp
=
SDPUtils
.
writeSessionBoilerplate
();
var
transceivers
=
[];
tracks
.
forEach
(
function
(
mline
,
sdpMLineIndex
)
{
// For each track, create an ice gatherer, ice transport,
// dtls transport, potentially rtpsender and rtpreceiver.
var
track
=
mline
.
track
;
var
kind
=
mline
.
kind
;
var
mid
=
SDPUtils
.
generateIdentifier
();
var
transports
=
self
.
usingBundle
&&
sdpMLineIndex
>
0
?
{
iceGatherer
:
transceivers
[
0
].
iceGatherer
,
iceTransport
:
transceivers
[
0
].
iceTransport
,
dtlsTransport
:
transceivers
[
0
].
dtlsTransport
}
:
self
.
_createIceAndDtlsTransports
(
mid
,
sdpMLineIndex
);
var
localCapabilities
=
RTCRtpSender
.
getCapabilities
(
kind
);
// filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
localCapabilities
.
codecs
=
localCapabilities
.
codecs
.
filter
(
function
(
codec
)
{
return
codec
.
name
!==
'
rtx
'
;
});
localCapabilities
.
codecs
.
forEach
(
function
(
codec
)
{
// work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
// by adding level-asymmetry-allowed=1
if
(
codec
.
name
===
'
H264
'
&&
codec
.
parameters
[
'
level-asymmetry-allowed
'
]
===
undefined
)
{
codec
.
parameters
[
'
level-asymmetry-allowed
'
]
=
'
1
'
;
}
});
var
rtpSender
;
var
rtpReceiver
;
// generate an ssrc now, to be used later in rtpSender.send
var
sendEncodingParameters
=
[{
ssrc
:
(
2
*
sdpMLineIndex
+
1
)
*
1001
}];
if
(
track
)
{
rtpSender
=
new
RTCRtpSender
(
track
,
transports
.
dtlsTransport
);
}
if
(
mline
.
wantReceive
)
{
rtpReceiver
=
new
RTCRtpReceiver
(
transports
.
dtlsTransport
,
kind
);
}
transceivers
[
sdpMLineIndex
]
=
{
iceGatherer
:
transports
.
iceGatherer
,
iceTransport
:
transports
.
iceTransport
,
dtlsTransport
:
transports
.
dtlsTransport
,
localCapabilities
:
localCapabilities
,
remoteCapabilities
:
null
,
rtpSender
:
rtpSender
,
rtpReceiver
:
rtpReceiver
,
kind
:
kind
,
mid
:
mid
,
sendEncodingParameters
:
sendEncodingParameters
,
recvEncodingParameters
:
null
};
});
if
(
this
.
usingBundle
)
{
sdp
+=
'
a=group:BUNDLE
'
+
transceivers
.
map
(
function
(
t
)
{
return
t
.
mid
;
}).
join
(
'
'
)
+
'
\r\n
'
;
}
tracks
.
forEach
(
function
(
mline
,
sdpMLineIndex
)
{
var
transceiver
=
transceivers
[
sdpMLineIndex
];
sdp
+=
SDPUtils
.
writeMediaSection
(
transceiver
,
transceiver
.
localCapabilities
,
'
offer
'
,
self
.
localStreams
[
0
]);
});
this
.
_pendingOffer
=
transceivers
;
var
desc
=
new
RTCSessionDescription
({
type
:
'
offer
'
,
sdp
:
sdp
});
if
(
arguments
.
length
&&
typeof
arguments
[
0
]
===
'
function
'
)
{
window
.
setTimeout
(
arguments
[
0
],
0
,
desc
);
}
return
Promise
.
resolve
(
desc
);
};
window
.
RTCPeerConnection
.
prototype
.
createAnswer
=
function
()
{
var
self
=
this
;
var
sdp
=
SDPUtils
.
writeSessionBoilerplate
();
if
(
this
.
usingBundle
)
{
sdp
+=
'
a=group:BUNDLE
'
+
this
.
transceivers
.
map
(
function
(
t
)
{
return
t
.
mid
;
}).
join
(
'
'
)
+
'
\r\n
'
;
}
this
.
transceivers
.
forEach
(
function
(
transceiver
)
{
if
(
transceiver
.
isDatachannel
)
{
sdp
+=
'
m=application 0 DTLS/SCTP 5000
\r\n
'
+
'
c=IN IP4 0.0.0.0
\r\n
'
+
'
a=mid:
'
+
transceiver
.
mid
+
'
\r\n
'
;
return
;
}
// Calculate intersection of capabilities.
var
commonCapabilities
=
self
.
_getCommonCapabilities
(
transceiver
.
localCapabilities
,
transceiver
.
remoteCapabilities
);
sdp
+=
SDPUtils
.
writeMediaSection
(
transceiver
,
commonCapabilities
,
'
answer
'
,
self
.
localStreams
[
0
]);
});
var
desc
=
new
RTCSessionDescription
({
type
:
'
answer
'
,
sdp
:
sdp
});
if
(
arguments
.
length
&&
typeof
arguments
[
0
]
===
'
function
'
)
{
window
.
setTimeout
(
arguments
[
0
],
0
,
desc
);
}
return
Promise
.
resolve
(
desc
);
};
window
.
RTCPeerConnection
.
prototype
.
addIceCandidate
=
function
(
candidate
)
{
if
(
!
candidate
)
{
for
(
var
j
=
0
;
j
<
this
.
transceivers
.
length
;
j
++
)
{
this
.
transceivers
[
j
].
iceTransport
.
addRemoteCandidate
({});
if
(
this
.
usingBundle
)
{
return
;
}
}
}
else
{
var
mLineIndex
=
candidate
.
sdpMLineIndex
;
if
(
candidate
.
sdpMid
)
{
for
(
var
i
=
0
;
i
<
this
.
transceivers
.
length
;
i
++
)
{
if
(
this
.
transceivers
[
i
].
mid
===
candidate
.
sdpMid
)
{
mLineIndex
=
i
;
break
;
}
}
}
var
transceiver
=
this
.
transceivers
[
mLineIndex
];
if
(
transceiver
)
{
var
cand
=
Object
.
keys
(
candidate
.
candidate
).
length
>
0
?
SDPUtils
.
parseCandidate
(
candidate
.
candidate
)
:
{};
// Ignore Chrome's invalid candidates since Edge does not like them.
if
(
cand
.
protocol
===
'
tcp
'
&&
(
cand
.
port
===
0
||
cand
.
port
===
9
))
{
return
;
}
// Ignore RTCP candidates, we assume RTCP-MUX.
if
(
cand
.
component
!==
'
1
'
)
{
return
;
}
transceiver
.
iceTransport
.
addRemoteCandidate
(
cand
);
// update the remoteDescription.
var
sections
=
SDPUtils
.
splitSections
(
this
.
remoteDescription
.
sdp
);
sections
[
mLineIndex
+
1
]
+=
(
cand
.
type
?
candidate
.
candidate
.
trim
()
:
'
a=end-of-candidates
'
)
+
'
\r\n
'
;
this
.
remoteDescription
.
sdp
=
sections
.
join
(
''
);
}
}
if
(
arguments
.
length
>
1
&&
typeof
arguments
[
1
]
===
'
function
'
)
{
window
.
setTimeout
(
arguments
[
1
],
0
);
}
return
Promise
.
resolve
();
};
window
.
RTCPeerConnection
.
prototype
.
getStats
=
function
()
{
var
promises
=
[];
this
.
transceivers
.
forEach
(
function
(
transceiver
)
{
[
'
rtpSender
'
,
'
rtpReceiver
'
,
'
iceGatherer
'
,
'
iceTransport
'
,
'
dtlsTransport
'
].
forEach
(
function
(
method
)
{
if
(
transceiver
[
method
])
{
promises
.
push
(
transceiver
[
method
].
getStats
());
}
});
});
var
cb
=
arguments
.
length
>
1
&&
typeof
arguments
[
1
]
===
'
function
'
&&
arguments
[
1
];
var
fixStatsType
=
function
(
stat
)
{
stat
.
type
=
{
inboundrtp
:
'
inbound-rtp
'
,
outboundrtp
:
'
outbound-rtp
'
,
candidatepair
:
'
candidate-pair
'
,
localcandidate
:
'
local-candidate
'
,
remotecandidate
:
'
remote-candidate
'
}[
stat
.
type
]
||
stat
.
type
;
return
stat
;
};
return
new
Promise
(
function
(
resolve
)
{
// shim getStats with maplike support
var
results
=
new
Map
();
Promise
.
all
(
promises
).
then
(
function
(
res
)
{
res
.
forEach
(
function
(
result
)
{
Object
.
keys
(
result
).
forEach
(
function
(
id
)
{
result
[
id
].
type
=
fixStatsType
(
result
[
id
]);
results
.
set
(
id
,
result
[
id
]);
});
});
if
(
cb
)
{
window
.
setTimeout
(
cb
,
0
,
results
);
}
resolve
(
results
);
});
});
};
}
};
// Expose public methods.
module
.
exports
=
{
shimPeerConnection
:
edgeShim
.
shimPeerConnection
,
shimGetUserMedia
:
require
(
'
./getusermedia
'
)
};
},{
"
../utils
"
:
10
,
"
./getusermedia
"
:
6
,
"
sdp
"
:
1
}],
6
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
// Expose public methods.
module
.
exports
=
function
()
{
var
shimError_
=
function
(
e
)
{
return
{
name
:
{
PermissionDeniedError
:
'
NotAllowedError
'
}[
e
.
name
]
||
e
.
name
,
message
:
e
.
message
,
constraint
:
e
.
constraint
,
toString
:
function
()
{
return
this
.
name
;
}
};
};
// getUserMedia error shim.
var
origGetUserMedia
=
navigator
.
mediaDevices
.
getUserMedia
.
bind
(
navigator
.
mediaDevices
);
navigator
.
mediaDevices
.
getUserMedia
=
function
(
c
)
{
return
origGetUserMedia
(
c
).
catch
(
function
(
e
)
{
return
Promise
.
reject
(
shimError_
(
e
));
});
};
};
},{}],
7
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
var
browserDetails
=
require
(
'
../utils
'
).
browserDetails
;
var
firefoxShim
=
{
shimOnTrack
:
function
()
{
if
(
typeof
window
===
'
object
'
&&
window
.
RTCPeerConnection
&&
!
(
'
ontrack
'
in
window
.
RTCPeerConnection
.
prototype
))
{
Object
.
defineProperty
(
window
.
RTCPeerConnection
.
prototype
,
'
ontrack
'
,
{
get
:
function
()
{
return
this
.
_ontrack
;
},
set
:
function
(
f
)
{
if
(
this
.
_ontrack
)
{
this
.
removeEventListener
(
'
track
'
,
this
.
_ontrack
);
this
.
removeEventListener
(
'
addstream
'
,
this
.
_ontrackpoly
);
}
this
.
addEventListener
(
'
track
'
,
this
.
_ontrack
=
f
);
this
.
addEventListener
(
'
addstream
'
,
this
.
_ontrackpoly
=
function
(
e
)
{
e
.
stream
.
getTracks
().
forEach
(
function
(
track
)
{
var
event
=
new
Event
(
'
track
'
);
event
.
track
=
track
;
event
.
receiver
=
{
track
:
track
};
event
.
streams
=
[
e
.
stream
];
this
.
dispatchEvent
(
event
);
}.
bind
(
this
));
}.
bind
(
this
));
}
});
}
},
shimSourceObject
:
function
()
{
// Firefox has supported mozSrcObject since FF22, unprefixed in 42.
if
(
typeof
window
===
'
object
'
)
{
if
(
window
.
HTMLMediaElement
&&
!
(
'
srcObject
'
in
window
.
HTMLMediaElement
.
prototype
))
{
// Shim the srcObject property, once, when HTMLMediaElement is found.
Object
.
defineProperty
(
window
.
HTMLMediaElement
.
prototype
,
'
srcObject
'
,
{
get
:
function
()
{
return
this
.
mozSrcObject
;
},
set
:
function
(
stream
)
{
this
.
mozSrcObject
=
stream
;
}
});
}
}
},
shimPeerConnection
:
function
()
{
if
(
typeof
window
!==
'
object
'
||
!
(
window
.
RTCPeerConnection
||
window
.
mozRTCPeerConnection
))
{
return
;
// probably media.peerconnection.enabled=false in about:config
}
// The RTCPeerConnection object.
if
(
!
window
.
RTCPeerConnection
)
{
window
.
RTCPeerConnection
=
function
(
pcConfig
,
pcConstraints
)
{
if
(
browserDetails
.
version
<
38
)
{
// .urls is not supported in FF < 38.
// create RTCIceServers with a single url.
if
(
pcConfig
&&
pcConfig
.
iceServers
)
{
var
newIceServers
=
[];
for
(
var
i
=
0
;
i
<
pcConfig
.
iceServers
.
length
;
i
++
)
{
var
server
=
pcConfig
.
iceServers
[
i
];
if
(
server
.
hasOwnProperty
(
'
urls
'
))
{
for
(
var
j
=
0
;
j
<
server
.
urls
.
length
;
j
++
)
{
var
newServer
=
{
url
:
server
.
urls
[
j
]
};
if
(
server
.
urls
[
j
].
indexOf
(
'
turn
'
)
===
0
)
{
newServer
.
username
=
server
.
username
;
newServer
.
credential
=
server
.
credential
;
}
newIceServers
.
push
(
newServer
);
}
}
else
{
newIceServers
.
push
(
pcConfig
.
iceServers
[
i
]);
}
}
pcConfig
.
iceServers
=
newIceServers
;
}
}
return
new
mozRTCPeerConnection
(
pcConfig
,
pcConstraints
);
};
window
.
RTCPeerConnection
.
prototype
=
mozRTCPeerConnection
.
prototype
;
// wrap static methods. Currently just generateCertificate.
if
(
mozRTCPeerConnection
.
generateCertificate
)
{
Object
.
defineProperty
(
window
.
RTCPeerConnection
,
'
generateCertificate
'
,
{
get
:
function
()
{
return
mozRTCPeerConnection
.
generateCertificate
;
}
});
}
window
.
RTCSessionDescription
=
mozRTCSessionDescription
;
window
.
RTCIceCandidate
=
mozRTCIceCandidate
;
}
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
[
'
setLocalDescription
'
,
'
setRemoteDescription
'
,
'
addIceCandidate
'
]
.
forEach
(
function
(
method
)
{
var
nativeMethod
=
RTCPeerConnection
.
prototype
[
method
];
RTCPeerConnection
.
prototype
[
method
]
=
function
()
{
arguments
[
0
]
=
new
((
method
===
'
addIceCandidate
'
)
?
RTCIceCandidate
:
RTCSessionDescription
)(
arguments
[
0
]);
return
nativeMethod
.
apply
(
this
,
arguments
);
};
});
// support for addIceCandidate(null or undefined)
var
nativeAddIceCandidate
=
RTCPeerConnection
.
prototype
.
addIceCandidate
;
RTCPeerConnection
.
prototype
.
addIceCandidate
=
function
()
{
if
(
!
arguments
[
0
])
{
if
(
arguments
[
1
])
{
arguments
[
1
].
apply
(
null
);
}
return
Promise
.
resolve
();
}
return
nativeAddIceCandidate
.
apply
(
this
,
arguments
);
};
// shim getStats with maplike support
var
makeMapStats
=
function
(
stats
)
{
var
map
=
new
Map
();
Object
.
keys
(
stats
).
forEach
(
function
(
key
)
{
map
.
set
(
key
,
stats
[
key
]);
map
[
key
]
=
stats
[
key
];
});
return
map
;
};
var
modernStatsTypes
=
{
inboundrtp
:
'
inbound-rtp
'
,
outboundrtp
:
'
outbound-rtp
'
,
candidatepair
:
'
candidate-pair
'
,
localcandidate
:
'
local-candidate
'
,
remotecandidate
:
'
remote-candidate
'
};
var
nativeGetStats
=
RTCPeerConnection
.
prototype
.
getStats
;
RTCPeerConnection
.
prototype
.
getStats
=
function
(
selector
,
onSucc
,
onErr
)
{
return
nativeGetStats
.
apply
(
this
,
[
selector
||
null
])
.
then
(
function
(
stats
)
{
if
(
browserDetails
.
version
<
48
)
{
stats
=
makeMapStats
(
stats
);
}
if
(
browserDetails
.
version
<
53
&&
!
onSucc
)
{
// Shim only promise getStats with spec-hyphens in type names
// Leave callback version alone; misc old uses of forEach before Map
stats
.
forEach
(
function
(
stat
)
{
stat
.
type
=
modernStatsTypes
[
stat
.
type
]
||
stat
.
type
;
});
}
return
stats
;
})
.
then
(
onSucc
,
onErr
);
};
}
};
// Expose public methods.
module
.
exports
=
{
shimOnTrack
:
firefoxShim
.
shimOnTrack
,
shimSourceObject
:
firefoxShim
.
shimSourceObject
,
shimPeerConnection
:
firefoxShim
.
shimPeerConnection
,
shimGetUserMedia
:
require
(
'
./getusermedia
'
)
};
},{
"
../utils
"
:
10
,
"
./getusermedia
"
:
8
}],
8
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
var
logging
=
require
(
'
../utils
'
).
log
;
var
browserDetails
=
require
(
'
../utils
'
).
browserDetails
;
// Expose public methods.
module
.
exports
=
function
()
{
var
shimError_
=
function
(
e
)
{
return
{
name
:
{
SecurityError
:
'
NotAllowedError
'
,
PermissionDeniedError
:
'
NotAllowedError
'
}[
e
.
name
]
||
e
.
name
,
message
:
{
'
The operation is insecure.
'
:
'
The request is not allowed by the
'
+
'
user agent or the platform in the current context.
'
}[
e
.
message
]
||
e
.
message
,
constraint
:
e
.
constraint
,
toString
:
function
()
{
return
this
.
name
+
(
this
.
message
&&
'
:
'
)
+
this
.
message
;
}
};
};
// getUserMedia constraints shim.
var
getUserMedia_
=
function
(
constraints
,
onSuccess
,
onError
)
{
var
constraintsToFF37_
=
function
(
c
)
{
if
(
typeof
c
!==
'
object
'
||
c
.
require
)
{
return
c
;
}
var
require
=
[];
Object
.
keys
(
c
).
forEach
(
function
(
key
)
{
if
(
key
===
'
require
'
||
key
===
'
advanced
'
||
key
===
'
mediaSource
'
)
{
return
;
}
var
r
=
c
[
key
]
=
(
typeof
c
[
key
]
===
'
object
'
)
?
c
[
key
]
:
{
ideal
:
c
[
key
]};
if
(
r
.
min
!==
undefined
||
r
.
max
!==
undefined
||
r
.
exact
!==
undefined
)
{
require
.
push
(
key
);
}
if
(
r
.
exact
!==
undefined
)
{
if
(
typeof
r
.
exact
===
'
number
'
)
{
r
.
min
=
r
.
max
=
r
.
exact
;
}
else
{
c
[
key
]
=
r
.
exact
;
}
delete
r
.
exact
;
}
if
(
r
.
ideal
!==
undefined
)
{
c
.
advanced
=
c
.
advanced
||
[];
var
oc
=
{};
if
(
typeof
r
.
ideal
===
'
number
'
)
{
oc
[
key
]
=
{
min
:
r
.
ideal
,
max
:
r
.
ideal
};
}
else
{
oc
[
key
]
=
r
.
ideal
;
}
c
.
advanced
.
push
(
oc
);
delete
r
.
ideal
;
if
(
!
Object
.
keys
(
r
).
length
)
{
delete
c
[
key
];
}
}
});
if
(
require
.
length
)
{
c
.
require
=
require
;
}
return
c
;
};
constraints
=
JSON
.
parse
(
JSON
.
stringify
(
constraints
));
if
(
browserDetails
.
version
<
38
)
{
logging
(
'
spec:
'
+
JSON
.
stringify
(
constraints
));
if
(
constraints
.
audio
)
{
constraints
.
audio
=
constraintsToFF37_
(
constraints
.
audio
);
}
if
(
constraints
.
video
)
{
constraints
.
video
=
constraintsToFF37_
(
constraints
.
video
);
}
logging
(
'
ff37:
'
+
JSON
.
stringify
(
constraints
));
}
return
navigator
.
mozGetUserMedia
(
constraints
,
onSuccess
,
function
(
e
)
{
onError
(
shimError_
(
e
));
});
};
// Returns the result of getUserMedia as a Promise.
var
getUserMediaPromise_
=
function
(
constraints
)
{
return
new
Promise
(
function
(
resolve
,
reject
)
{
getUserMedia_
(
constraints
,
resolve
,
reject
);
});
};
// Shim for mediaDevices on older versions.
if
(
!
navigator
.
mediaDevices
)
{
navigator
.
mediaDevices
=
{
getUserMedia
:
getUserMediaPromise_
,
addEventListener
:
function
()
{
},
removeEventListener
:
function
()
{
}
};
}
navigator
.
mediaDevices
.
enumerateDevices
=
navigator
.
mediaDevices
.
enumerateDevices
||
function
()
{
return
new
Promise
(
function
(
resolve
)
{
var
infos
=
[
{
kind
:
'
audioinput
'
,
deviceId
:
'
default
'
,
label
:
''
,
groupId
:
''
},
{
kind
:
'
videoinput
'
,
deviceId
:
'
default
'
,
label
:
''
,
groupId
:
''
}
];
resolve
(
infos
);
});
};
if
(
browserDetails
.
version
<
41
)
{
// Work around http://bugzil.la/1169665
var
orgEnumerateDevices
=
navigator
.
mediaDevices
.
enumerateDevices
.
bind
(
navigator
.
mediaDevices
);
navigator
.
mediaDevices
.
enumerateDevices
=
function
()
{
return
orgEnumerateDevices
().
then
(
undefined
,
function
(
e
)
{
if
(
e
.
name
===
'
NotFoundError
'
)
{
return
[];
}
throw
e
;
});
};
}
if
(
browserDetails
.
version
<
49
)
{
var
origGetUserMedia
=
navigator
.
mediaDevices
.
getUserMedia
.
bind
(
navigator
.
mediaDevices
);
navigator
.
mediaDevices
.
getUserMedia
=
function
(
c
)
{
return
origGetUserMedia
(
c
).
then
(
function
(
stream
)
{
// Work around https://bugzil.la/802326
if
(
c
.
audio
&&
!
stream
.
getAudioTracks
().
length
||
c
.
video
&&
!
stream
.
getVideoTracks
().
length
)
{
stream
.
getTracks
().
forEach
(
function
(
track
)
{
track
.
stop
();
});
throw
new
DOMException
(
'
The object can not be found here.
'
,
'
NotFoundError
'
);
}
return
stream
;
},
function
(
e
)
{
return
Promise
.
reject
(
shimError_
(
e
));
});
};
}
navigator
.
getUserMedia
=
function
(
constraints
,
onSuccess
,
onError
)
{
if
(
browserDetails
.
version
<
44
)
{
return
getUserMedia_
(
constraints
,
onSuccess
,
onError
);
}
// Replace Firefox 44+'s deprecation warning with unprefixed version.
console
.
warn
(
'
navigator.getUserMedia has been replaced by
'
+
'
navigator.mediaDevices.getUserMedia
'
);
navigator
.
mediaDevices
.
getUserMedia
(
constraints
).
then
(
onSuccess
,
onError
);
};
};
},{
"
../utils
"
:
10
}],
9
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'
use strict
'
;
var
safariShim
=
{
// TODO: DrAlex, should be here, double check against LayoutTests
// shimOnTrack: function() { },
// TODO: once the back-end for the mac port is done, add.
// TODO: check for webkitGTK+
// shimPeerConnection: function() { },
shimGetUserMedia
:
function
()
{
navigator
.
getUserMedia
=
navigator
.
webkitGetUserMedia
;
}
};
// Expose public methods.
module
.
exports
=
{
shimGetUserMedia
:
safariShim
.
shimGetUserMedia
// TODO
// shimOnTrack: safariShim.shimOnTrack,
// shimPeerConnection: safariShim.shimPeerConnection
};
},{}],
10
:[
function
(
require
,
module
,
exports
){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'
use strict
'
;
var
logDisabled_
=
true
;
// Utility methods.
var
utils
=
{
disableLog
:
function
(
bool
)
{
if
(
typeof
bool
!==
'
boolean
'
)
{
return
new
Error
(
'
Argument type:
'
+
typeof
bool
+
'
. Please use a boolean.
'
);
}
logDisabled_
=
bool
;
return
(
bool
)
?
'
adapter.js logging disabled
'
:
'
adapter.js logging enabled
'
;
},
log
:
function
()
{
if
(
typeof
window
===
'
object
'
)
{
if
(
logDisabled_
)
{
return
;
}
if
(
typeof
console
!==
'
undefined
'
&&
typeof
console
.
log
===
'
function
'
)
{
console
.
log
.
apply
(
console
,
arguments
);
}
}
},
/**
* Extract browser version out of the provided user agent string.
*
* @param {!string} uastring userAgent string.
* @param {!string} expr Regular expression used as match criteria.
* @param {!number} pos position in the version string to be returned.
* @return {!number} browser version.
*/
extractVersion
:
function
(
uastring
,
expr
,
pos
)
{
var
match
=
uastring
.
match
(
expr
);
return
match
&&
match
.
length
>=
pos
&&
parseInt
(
match
[
pos
],
10
);
},
/**
* Browser detector.
*
* @return {object} result containing browser and version
* properties.
*/
detectBrowser
:
function
()
{
// Returned result object.
var
result
=
{};
result
.
browser
=
null
;
result
.
version
=
null
;
// Fail early if it's not a browser
if
(
typeof
window
===
'
undefined
'
||
!
window
.
navigator
)
{
result
.
browser
=
'
Not a browser.
'
;
return
result
;
}
// Firefox.
if
(
navigator
.
mozGetUserMedia
)
{
result
.
browser
=
'
firefox
'
;
result
.
version
=
this
.
extractVersion
(
navigator
.
userAgent
,
/Firefox
\/([
0-9
]
+
)\.
/
,
1
);
// all webkit-based browsers
}
else
if
(
navigator
.
webkitGetUserMedia
)
{
// Chrome, Chromium, Webview, Opera, all use the chrome shim for now
if
(
window
.
webkitRTCPeerConnection
)
{
result
.
browser
=
'
chrome
'
;
result
.
version
=
this
.
extractVersion
(
navigator
.
userAgent
,
/Chrom
(
e|ium
)\/([
0-9
]
+
)\.
/
,
2
);
// Safari or unknown webkit-based
// for the time being Safari has support for MediaStreams but not webRTC
}
else
{
// Safari UA substrings of interest for reference:
// - webkit version: AppleWebKit/602.1.25 (also used in Op,Cr)
// - safari UI version: Version/9.0.3 (unique to Safari)
// - safari UI webkit version: Safari/601.4.4 (also used in Op,Cr)
//
// if the webkit version and safari UI webkit versions are equals,
// ... this is a stable version.
//
// only the internal webkit version is important today to know if
// media streams are supported
//
if
(
navigator
.
userAgent
.
match
(
/Version
\/(\d
+
)
.
(\d
+
)
/
))
{
result
.
browser
=
'
safari
'
;
result
.
version
=
this
.
extractVersion
(
navigator
.
userAgent
,
/AppleWebKit
\/([
0-9
]
+
)\.
/
,
1
);
// unknown webkit-based browser
}
else
{
result
.
browser
=
'
Unsupported webkit-based browser
'
+
'
with GUM support but no WebRTC support.
'
;
return
result
;
}
}
// Edge.
}
else
if
(
navigator
.
mediaDevices
&&
navigator
.
userAgent
.
match
(
/Edge
\/(\d
+
)
.
(\d
+
)
$/
))
{
result
.
browser
=
'
edge
'
;
result
.
version
=
this
.
extractVersion
(
navigator
.
userAgent
,
/Edge
\/(\d
+
)
.
(\d
+
)
$/
,
2
);
// Default fallthrough: not supported.
}
else
{
result
.
browser
=
'
Not a supported browser.
'
;
return
result
;
}
return
result
;
},
shimCreateObjectURL
:
function
()
{
var
nativeCreateObjectURL
=
URL
.
createObjectURL
.
bind
(
URL
);
var
nativeRevokeObjectURL
=
URL
.
revokeObjectURL
.
bind
(
URL
);
var
streams
=
new
Map
(),
newId
=
0
;
URL
.
createObjectURL
=
function
(
stream
)
{
if
(
'
getTracks
'
in
stream
)
{
var
url
=
'
polyblob:
'
+
(
++
newId
);
streams
.
set
(
url
,
stream
);
console
.
log
(
'
URL.createObjectURL(stream) is deprecated!
'
+
'
Use elem.srcObject = stream instead!
'
);
return
url
;
}
return
nativeCreateObjectURL
(
stream
);
};
URL
.
revokeObjectURL
=
function
(
url
)
{
nativeRevokeObjectURL
(
url
);
streams
.
delete
(
url
);
};
var
dsc
=
Object
.
getOwnPropertyDescriptor
(
window
.
HTMLMediaElement
.
prototype
,
'
src
'
);
Object
.
defineProperty
(
window
.
HTMLMediaElement
.
prototype
,
'
src
'
,
{
get
:
function
()
{
return
dsc
.
get
.
apply
(
this
);
},
set
:
function
(
url
)
{
this
.
srcObject
=
streams
.
get
(
url
)
||
null
;
return
dsc
.
set
.
apply
(
this
,
[
url
]);
}
});
}
};
// Export.
module
.
exports
=
{
log
:
utils
.
log
,
disableLog
:
utils
.
disableLog
,
browserDetails
:
utils
.
detectBrowser
(),
extractVersion
:
utils
.
extractVersion
,
shimCreateObjectURL
:
utils
.
shimCreateObjectURL
};
},{}]},{},[
2
])(
2
)
});
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/adapter_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
adapter.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
adapter_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<string>
en
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
WebRTC Adapter JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
001
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/fast_priority_queue_js.js
0 → 100644
View file @
edcd20f8
/**
* FastPriorityQueue.js : a fast heap-based priority queue in JavaScript.
* (c) the authors
* Licensed under the Apache License, Version 2.0.
*
* Speed-optimized heap-based priority queue for modern browsers and JavaScript engines.
*
* Usage :
Installation (in shell, if you use node):
$ npm install fastpriorityqueue
Running test program (in JavaScript):
// var FastPriorityQueue = require("fastpriorityqueue");// in node
var x = new FastPriorityQueue();
x.add(1);
x.add(0);
x.add(5);
x.add(4);
x.add(3);
x.peek(); // should return 0, leaves x unchanged
x.size; // should return 5, leaves x unchanged
while(!x.isEmpty()) {
console.log(x.poll());
} // will print 0 1 3 4 5
x.trim(); // (optional) optimizes memory usage
*/
"
use strict
"
;
var
defaultcomparator
=
function
(
a
,
b
)
{
return
a
<
b
;
};
// the provided comparator function should take a, b and return *true* when a < b
function
FastPriorityQueue
(
comparator
)
{
this
.
array
=
[];
this
.
size
=
0
;
this
.
compare
=
comparator
||
defaultcomparator
;
}
// Add an element the the queue
// runs in O(log n) time
FastPriorityQueue
.
prototype
.
add
=
function
(
myval
)
{
var
i
=
this
.
size
;
this
.
array
[
this
.
size
]
=
myval
;
this
.
size
+=
1
;
var
p
;
var
ap
;
while
(
i
>
0
)
{
p
=
(
i
-
1
)
>>
1
;
ap
=
this
.
array
[
p
];
if
(
!
this
.
compare
(
myval
,
ap
))
{
break
;
}
this
.
array
[
i
]
=
ap
;
i
=
p
;
}
this
.
array
[
i
]
=
myval
;
};
// replace the content of the heap by provided array and "heapifies it"
FastPriorityQueue
.
prototype
.
heapify
=
function
(
arr
)
{
this
.
array
=
arr
;
this
.
size
=
arr
.
length
;
var
i
;
for
(
i
=
(
this
.
size
>>
1
);
i
>=
0
;
i
--
)
{
this
.
_percolateDown
(
i
);
}
};
// for internal use
FastPriorityQueue
.
prototype
.
_percolateUp
=
function
(
i
)
{
var
myval
=
this
.
array
[
i
];
var
p
;
var
ap
;
while
(
i
>
0
)
{
p
=
(
i
-
1
)
>>
1
;
ap
=
this
.
array
[
p
];
if
(
!
this
.
compare
(
myval
,
ap
))
{
break
;
}
this
.
array
[
i
]
=
ap
;
i
=
p
;
}
this
.
array
[
i
]
=
myval
;
};
// for internal use
FastPriorityQueue
.
prototype
.
_percolateDown
=
function
(
i
)
{
var
size
=
this
.
size
;
var
hsize
=
this
.
size
>>>
1
;
var
ai
=
this
.
array
[
i
];
var
l
;
var
r
;
var
bestc
;
while
(
i
<
hsize
)
{
l
=
(
i
<<
1
)
+
1
;
r
=
l
+
1
;
bestc
=
this
.
array
[
l
];
if
(
r
<
size
)
{
if
(
this
.
compare
(
this
.
array
[
r
],
bestc
))
{
l
=
r
;
bestc
=
this
.
array
[
r
];
}
}
if
(
!
this
.
compare
(
bestc
,
ai
))
{
break
;
}
this
.
array
[
i
]
=
bestc
;
i
=
l
;
}
this
.
array
[
i
]
=
ai
;
};
// Look at the top of the queue (a smallest element)
// executes in constant time
//
// This function assumes that the priority queue is
// not empty and the caller is resposible for the check.
// You can use an expression such as
// "isEmpty() ? undefined : peek()"
// if you expect to be calling peek on an empty priority queue.
//
FastPriorityQueue
.
prototype
.
peek
=
function
()
{
return
this
.
array
[
0
];
};
// remove the element on top of the heap (a smallest element)
// runs in logarithmic time
//
//
// This function assumes that the priority queue is
// not empty, and the caller is responsible for the check.
// You can use an expression such as
// "isEmpty() ? undefined : poll()"
// if you expect to be calling poll on an empty priority queue.
//
// For long-running and large priority queues, or priority queues
// storing large objects, you may want to call the trim function
// at strategic times to recover allocated memory.
FastPriorityQueue
.
prototype
.
poll
=
function
()
{
var
ans
=
this
.
array
[
0
];
if
(
this
.
size
>
1
)
{
this
.
array
[
0
]
=
this
.
array
[
--
this
.
size
];
this
.
_percolateDown
(
0
|
0
);
}
else
{
this
.
size
-=
1
;
}
return
ans
;
};
// recover unused memory (for long-running priority queues)
FastPriorityQueue
.
prototype
.
trim
=
function
()
{
this
.
array
=
this
.
array
.
slice
(
0
,
this
.
size
);
};
// Check whether the heap is empty
FastPriorityQueue
.
prototype
.
isEmpty
=
function
()
{
return
this
.
size
===
0
;
};
// just for illustration purposes
var
main
=
function
()
{
// main code
var
x
=
new
FastPriorityQueue
(
function
(
a
,
b
)
{
return
a
<
b
;
});
x
.
add
(
1
);
x
.
add
(
0
);
x
.
add
(
5
);
x
.
add
(
4
);
x
.
add
(
3
);
while
(
!
x
.
isEmpty
())
{
console
.
log
(
x
.
poll
());
}
};
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/fast_priority_queue_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
fast_priority_queue.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
fast_priority_queue_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
Fast Priority Queue JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
<title>
OfficeJS Chat Panel
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"gadget_erp5_chat_panel.js"
></script>
</head>
<body>
<header
class=
"panel-header"
>
<button>
<span
class=
"fa fa-times"
aria-hidden=
"true"
></span>
</button>
<a
href=
"#"
>
<img
alt=
"ERP5 Logo"
src=
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJcAAAA/CAMAAADaDqrIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowMEM5NUE4MzQ5NjQxMUUzOUZEQUU2NUY1RTI1RjdCQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowMEM5NUE4NDQ5NjQxMUUzOUZEQUU2NUY1RTI1RjdCQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAwQzk1QTgxNDk2NDExRTM5RkRBRTY1RjVFMjVGN0JCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjAwQzk1QTgyNDk2NDExRTM5RkRBRTY1RjVFMjVGN0JCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+J9MJsAAAAwBQTFRF///////M//+Z//9m//8z//8A/8z//8zM/8yZ/8xm/8wz/8wA/5n//5nM/5mZ/5lm/5kz/5kA/2b//2bM/2aZ/2Zm/2Yz/2YA/zP//zPM/zOZ/zNm/zMz/zMA/wD//wDM/wCZ/wBm/wAz/wAAzP//zP/MzP+ZzP9mzP8zzP8AzMz/zMzMzMyZzMxmzMwzzMwAzJn/zJnMzJmZzJlmzJkzzJkAzGb/zGbMzGaZzGZmzGYzzGYAzDP/zDPMzDOZzDNmzDMzzDMAzAD/zADMzACZzABmzAAzzAAAmf//mf/Mmf+Zmf9mmf8zmf8Amcz/mczMmcyZmcxmmcwzmcwAmZn/mZnMmZmZmZlmmZkzmZkAmWb/mWbMmWaZmWZmmWYzmWYAmTP/mTPMmTOZmTNmmTMzmTMAmQD/mQDMmQCZmQBmmQAzmQAAZv//Zv/MZv+ZZv9mZv8zZv8AZsz/ZszMZsyZZsxmZswzZswAZpn/ZpnMZpmZZplmZpkzZpkAZmb/ZmbMZmaZZmZmZmYzZmYAZjP/ZjPMZjOZZjNmZjMzZjMAZgD/ZgDMZgCZZgBmZgAzZgAAM///M//MM/+ZM/9mM/8zM/8AM8z/M8zMM8yZM8xmM8wzM8wAM5n/M5nMM5mZM5lmM5kzM5kAM2b/M2bMM2aZM2ZmM2YzM2YAMzP/MzPMMzOZMzNmMzMzMzMAMwD/MwDMMwCZMwBmMwAzMwAAAP//AP/MAP+ZAP9mAP8zAP8AAMz/AMzMAMyZAMxmAMwzAMwAAJn/AJnMAJmZAJlmAJkzAJkAAGb/AGbMAGaZAGZmAGYzAGYAADP/ADPMADOZADNmADMzADMAAAD/AADMAACZAABmAAAzAAAAHHa7K3/AOojESZHJWZvNaKTSd63Whrbblb/fpMjks9Howtrt4e320uTx8Pb6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdfKHSQAAAOh0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ALItoLoAAAJkSURBVHja7NlZsqsgEABQ979C5kGGddxo4os0Q8BAynoFn0nUU23TNGRB9xzLdE3XdE3XdP2fLnIzF2VSrsYvC72HizAh9eZ5DVuOl15S4/jWwC+kDC8HPzCSJVHcRY8QXV2PwQuujUYTsYrvYlBv1yKLrsXHsPghnvR3Lazoip/JKuwpF8sm6/bY01Ow9CBlTXi53PNoRcUktEZT1NV1PIPnXAjtMJIMlzerFIzU1dVWF7aPj3Tetae4gjdxktG2et/qQjJ4kZFrv6ED4bK4eR1qduHzvEi4+PbGwD0EGu9CQQLFrv1F4jBc6BcuW3YhkPiPX1hOfxQvnHfh0HVMRqc4HuqiQf5k3mO6dq1s8Hx0H/Le5kq9YaNcxAcrZLpOvAv+ClcgNcZFtnrucd5Fwx4mXj0drnbBYXMuJmFDAV3Uhm4qlbHh3ddOrmholHNxFTVC+xTl2mf6pI4uVe4LwSp0DPGOmh/hAnM94bKZjkHlWuiLeV+cTab4luMKk82wy/Px2fLJskvKUk03hRf9RZ0wiYiZ4uVwVwTb9E71y0XboTYXGuTaa33Y47W5zBjXM3P11y7TfR2ycFdxyaW7uwSc5m0un9ix91m3TWJfW+1ijfW+wcVAfjS5dOP62LyvZZdcLHnA1MkFAtbgwq+Vm47po8OA1buwyy/a1ec5urzveAes2sV8kfW963mFaHNhlT0b6+Yi5+StchH1CtaK0ThXELDPLiyOPZHldzm/J1z9O/B14g7/K+DtkP7U8ivSeD4xaPCTSQvy+YIfubZWzRgtRe1iMP+3mq7pmq7p6jv+BBgAPrgi/TzwWzkAAAAASUVORK5CYII="
/>
</a>
</header>
<article
class=
"panel-listview"
>
<ul>
<li>
<a
class=
"panel-element"
href=
"#!display?n.page=contact_list"
>
<span
class=
"fa fa-home"
aria-hidden=
"true"
></span>
Contact List
</a>
</li>
<li>
<a
class=
"panel-element"
href=
"#!display?n.page=chat_box"
>
<span
class=
"fa fa-commenting-o"
aria-hidden=
"true"
></span>
Chat Box
</a>
</li>
<li>
<a
class=
"panel-element"
href=
"#!display?n.page=jio_configurator"
>
<span
class=
"fa fa-exchange"
aria-hidden=
"true"
></span>
Storage Configuration
</a>
</li>
<li>
<a
class=
"panel-element"
href=
"#!display?n.page=sync"
>
<span
class=
"fa fa-cogs"
aria-hidden=
"true"
></span>
Synchronization
</a>
</li>
</ul>
</article>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
<string>
contributor/person_module/1
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_chat_panel.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_chat_panel_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Chat Panel
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_js.js
0 → 100644
View file @
edcd20f8
/* global window, RSVP, rJS */
/* jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80 */
(
function
(
window
,
RSVP
,
rJS
)
{
'
use strict
'
;
rJS
(
window
)
.
setState
({
// Boolean to indicate whether the panel is shown on the screen
visible
:
false
,
})
.
declareMethod
(
'
render
'
,
function
()
{})
// Toggle the panel between shown and hidden
.
declareMethod
(
'
toggle
'
,
function
()
{
const
gadget
=
this
;
return
gadget
.
changeState
({
visible
:
!
gadget
.
state
.
visible
});
})
// Collapse the panel into being hidden
.
declareMethod
(
'
close
'
,
function
()
{
const
gadget
=
this
;
return
gadget
.
changeState
({
visible
:
false
});
})
// Handle panel visibility changes
.
onStateChange
(
function
()
{
const
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
gadget
.
state
.
visible
&&
!
gadget
.
element
.
classList
.
contains
(
'
visible
'
))
{
gadget
.
element
.
classList
.
toggle
(
'
visible
'
);
}
else
if
(
!
gadget
.
state
.
visible
&&
gadget
.
element
.
classList
.
contains
(
'
visible
'
))
{
gadget
.
element
.
classList
.
remove
(
'
visible
'
);
}
return
;
});
})
/* Collapse the panel whenever any button is clicked,
* since there should be only one button, close;
* set all parameters to false to click other links
*/
.
onEvent
(
'
click
'
,
function
(
event
)
{
const
gadget
=
this
;
if
(
event
.
target
.
tagName
===
'
BUTTON
'
)
{
return
gadget
.
close
();
}
else
{
return
;
}
},
false
,
false
);
}(
window
,
RSVP
,
rJS
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_panel_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
<string>
contributor/person_module/1
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_chat_panel.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_chat_panel_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Chat Panel JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
<title>
>WebRTC Gadget
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"adapter.js"
></script>
<script
src=
"gadget_global.js"
></script>
<script
src=
"gadget_erp5_chat_webrtc.js"
></script>
</head>
<body>
<p
class=
"error"
></p>
<p
class=
"status"
></p>
<form
class=
"contact-form"
>
<label>
E-mail Address:
</label>
<input
type=
"text"
name=
"email"
/>
<label>
Dropbox folder:
</label>
<input
type=
"text"
name=
"dropbox_folder"
placeholder=
"/Apps/OfficeJS Chat/"
/>
<label>
ERP5 URL:
</label>
<input
type=
"text"
name=
"erp5_url"
placeholder=
"https://softinst75770.host.vifib.net/erp5/web_site_module/"
/>
<br
/>
<input
type=
"submit"
value=
"Update information!"
/>
</form>
<form
class=
"download-form"
>
<input
type=
"submit"
value=
"Download information from remote storage!"
/>
</form>
<form
class=
"auth-form"
>
<label>
<input
type=
"radio"
name=
"auth"
value=
"erp5"
required=
"required"
/>
ERP5
</label>
<label>
<input
type=
"radio"
name=
"auth"
value=
"dropbox"
required=
"required"
/>
Dropbox
</label>
<input
type=
"submit"
value=
"Authenticate!"
/>
</form>
<form
class=
"host-offer-form"
>
<label>
Paste your guest's offer in this box:
</label>
<textarea
rows=
"5"
cols=
"80"
name=
"send"
></textarea>
<input
type=
"submit"
value=
"Paste it!"
/>
</form>
<form
class=
"host-answer-form"
>
<p>
This is your answer. Send it to your guest!
</p>
<textarea
rows=
"5"
cols=
"80"
name=
"receive"
readonly
></textarea>
</form>
<form
class=
"guest-offer-form"
>
<p>
This is your new offer. Send it to your host!
</p>
<textarea
rows=
"5"
cols=
"80"
name=
"receive"
readonly
></textarea>
<input
type=
"submit"
value=
"I sent it to my host."
/>
</form>
<form
class=
"guest-answer-form"
>
<label>
Now, paste your host's answer in this box:
</label>
<textarea
rows=
"5"
cols=
"80"
name=
"send"
></textarea>
<input
type=
"submit"
value=
"Paste it!"
/>
</form>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
<string>
contributor/person_module/1
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_chat_webrtc.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_chat_webrtc_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Chat WebRTC
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_js.js
0 → 100644
View file @
edcd20f8
/* global window, document, RSVP, rJS */
/* jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80 */
(
function
(
window
,
document
,
RSVP
,
rJS
)
{
"
use strict
"
;
function
logError
(
error
)
{
console
.
log
(
error
);
}
function
logQueue
(
action
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
action
;
})
.
push
(
null
,
logError
);
}
function
styleElementByQuery
(
gadget
,
query
,
style
)
{
gadget
.
element
.
querySelector
(
query
).
style
.
display
=
style
;
}
function
resetInputValue
(
element
)
{
const
value
=
element
.
value
;
element
.
value
=
""
;
return
value
;
}
function
getQueryValue
(
query_list
,
query_string
)
{
for
(
let
i
=
0
,
i_len
=
query_list
.
length
;
i
<
i_len
;
i
++
)
{
const
query
=
query_list
[
i
];
if
(
query_string
.
indexOf
(
query
+
"
=
"
)
!==
-
1
)
{
const
start
=
query_string
.
indexOf
(
query
+
"
=
"
)
+
query
.
length
+
1
;
let
end
=
query_string
.
indexOf
(
"
&
"
,
start
);
if
(
end
===
-
1
)
{
end
=
query_string
.
length
;
}
return
query_string
.
slice
(
start
,
end
);
}
}
return
null
;
}
function
setQueryValue
(
query_list
,
query_string
,
element
)
{
const
value
=
getQueryValue
(
query_list
,
query_string
);
if
(
value
)
{
element
.
value
=
value
;
}
}
function
pollUntilNotNull
(
gadget
,
delay_ms
,
timeout_ms
,
nullableFunction
,
callbackFunction
)
{
if
(
callbackFunction
===
undefined
)
{
callbackFunction
=
function
()
{};
}
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
nullableFunction
();
})
.
push
(
function
(
result
)
{
if
(
result
!==
null
)
{
return
callbackFunction
(
result
);
}
else
{
return
RSVP
.
any
([
RSVP
.
timeout
(
timeout_ms
),
promiseDoWhile
(
function
()
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
RSVP
.
delay
(
delay_ms
);
})
.
push
(
function
()
{
return
nullableFunction
();
})
.
push
(
function
(
result
)
{
if
(
result
===
null
)
{
return
null
;
}
else
{
return
callbackFunction
(
result
);
}
})
.
push
(
function
(
nullable
)
{
return
nullable
===
null
;
})
.
push
(
null
,
logError
);
})
]);
}
});
}
function
webrtcError
(
gadget
,
error_string
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
gadget
.
element
.
querySelector
(
"
.error
"
).
textContent
=
error_string
;
})
.
push
(
function
()
{
return
RSVP
.
delay
(
10000
);
})
.
push
(
function
()
{
return
error_string
;
});
}
// jIO utility functions
function
createDropboxJio
(
gadget
,
param_dict
)
{
let
dropbox_gadget
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
declareGadget
(
"
gadget_jio.html
"
,
{
scope
:
"
dropbox_gadget
"
});
})
.
push
(
function
(
jio_gadget
)
{
dropbox_gadget
=
jio_gadget
;
const
start
=
document
.
URL
.
indexOf
(
"
access_token=
"
)
+
13
;
const
end
=
document
.
URL
.
indexOf
(
"
&
"
,
start
);
const
token
=
document
.
URL
.
slice
(
start
,
end
);
return
dropbox_gadget
.
createJio
({
type
:
"
dropbox
"
,
access_token
:
token
,
root
:
"
dropbox
"
,
});
})
.
push
(
function
()
{
return
dropbox_gadget
.
put
(
param_dict
.
dropbox_folder
,
{});
})
.
push
(
null
,
logError
);
}
function
resetDropboxContent
(
dropbox_gadget
,
param_dict
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
dropbox_gadget
.
allAttachments
(
param_dict
.
dropbox_folder
);
})
.
push
(
function
(
negotiation_list
)
{
const
promise_list
=
[];
for
(
let
file_name
in
negotiation_list
)
{
if
(
negotiation_list
.
hasOwnProperty
(
file_name
)
&&
(
file_name
.
indexOf
(
"
offer_
"
+
param_dict
.
room
+
"
_
"
)
===
0
||
file_name
.
indexOf
(
"
answer_
"
+
param_dict
.
room
+
"
_
"
)
===
0
))
{
promise_list
.
push
(
dropbox_gadget
.
removeAttachment
(
param_dict
.
dropbox_folder
,
file_name
));
}
}
return
RSVP
.
all
(
promise_list
);
})
.
push
(
null
,
logError
);
}
function
getDropboxOffer
(
dropbox_gadget
,
param_dict
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
dropbox_gadget
.
allAttachments
(
param_dict
.
dropbox_folder
);
})
.
push
(
function
(
negotiation_list
)
{
for
(
let
file_name
in
negotiation_list
)
{
if
(
negotiation_list
.
hasOwnProperty
(
file_name
)
&&
file_name
.
indexOf
(
"
offer_
"
+
param_dict
.
room
+
"
_
"
)
===
0
)
{
return
file_name
.
slice
(
6
);
}
}
return
null
;
})
.
push
(
null
,
logError
);
}
function
getDropboxContent
(
dropbox_gadget
,
param_dict
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
dropbox_gadget
.
getAttachment
(
param_dict
.
dropbox_folder
,
param_dict
.
file_name
);
})
.
push
(
function
(
attachment
)
{
return
promiseReadAsText
(
attachment
);
},
function
()
{
return
null
;
})
.
push
(
null
,
logError
);
}
function
putDropboxContent
(
dropbox_gadget
,
param_dict
)
{
return
logQueue
(
dropbox_gadget
.
putAttachment
(
param_dict
.
dropbox_folder
,
param_dict
.
file_name
,
new
Blob
([
param_dict
.
content
],
{
type
:
"
text
"
})));
}
function
removeDropboxContent
(
dropbox_gadget
,
param_dict
)
{
return
logQueue
(
dropbox_gadget
.
removeAttachment
(
param_dict
.
dropbox_folder
,
param_dict
.
file_name
));
}
function
authenticateDropbox
(
gadget
)
{
const
dropbox_param_dict
=
{
gadget
:
gadget
,
gadget_name
:
"
dropbox_gadget
"
,
room
:
gadget
.
state
.
login_dict
.
room
,
name
:
gadget
.
state
.
login_dict
.
name
,
file_name
:
null
,
content
:
null
,
dropbox_folder
:
gadget
.
state
.
dropbox_folder
,
function_dict
:
{
resetContent
:
resetDropboxContent
,
getOffer
:
getDropboxOffer
,
getContent
:
getDropboxContent
,
putContent
:
putDropboxContent
,
removeContent
:
removeDropboxContent
,
},
};
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
document
.
URL
.
indexOf
(
"
access_token=
"
)
===
-
1
)
{
throw
webrtcError
(
gadget
,
"
Please log in to Dropbox!
"
);
}
else
{
return
createDropboxJio
(
gadget
,
dropbox_param_dict
);
}
})
.
push
(
function
()
{
return
gadget
.
getDeclaredGadget
(
"
dropbox_gadget
"
);
})
.
push
(
function
(
dropbox_gadget
)
{
return
resetDropboxContent
(
dropbox_gadget
,
dropbox_param_dict
);
})
.
push
(
function
()
{
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
return
authenticateHost
(
dropbox_param_dict
);
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
return
authenticateGuest
(
dropbox_param_dict
);
}
})
.
push
(
null
,
logError
);
}
function
createErp5Jio
(
gadget
,
param_dict
)
{
let
erp5_gadget
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
declareGadget
(
"
gadget_jio.html
"
,
{
scope
:
"
erp5_gadget
"
});
})
.
push
(
function
(
jio_gadget
)
{
erp5_gadget
=
jio_gadget
;
return
erp5_gadget
.
createJio
({
type
:
"
erp5
"
,
url
:
(
new
URI
(
"
hateoas
"
)).
absoluteTo
(
param_dict
.
erp5_url
).
toString
(),
default_view_reference
:
"
view
"
,
});
})
.
push
(
function
()
{
// Must use double quotation marks in query string!
return
erp5_gadget
.
allDocs
({
limit
:
[
0
,
1000000
],
query
:
'
portal_type: "Webrtc Room"
'
,
sort_on
:
[[
"
last_modified
"
,
"
descending
"
]],
select_list
:
[
"
title
"
],
});
})
.
push
(
function
(
room_list
)
{
if
(
room_list
.
data
.
rows
.
length
)
{
for
(
let
i
=
0
,
i_len
=
room_list
.
data
.
rows
.
length
;
i
<
i_len
;
i
++
)
{
if
(
room_list
.
data
.
rows
[
i
].
value
.
title
===
param_dict
.
room
)
{
return
room_list
.
data
.
rows
[
i
].
id
;
}
}
}
return
erp5_gadget
.
post
({
portal_type
:
"
Webrtc Room
"
,
parent_relative_url
:
"
webrtc_rooms_module
"
,
title
:
param_dict
.
room
,
negotiation_list
:
JSON
.
stringify
({}),
});
})
.
push
(
null
,
logError
);
}
function
resetErp5Content
(
erp5_gadget
,
param_dict
)
{
return
logQueue
(
erp5_gadget
.
put
(
param_dict
.
id
,
{
portal_type
:
"
Webrtc Room
"
,
parent_relative_url
:
"
webrtc_rooms_module
"
,
title
:
param_dict
.
room
,
negotiation_list
:
JSON
.
stringify
({}),
}));
}
function
getErp5Offer
(
erp5_gadget
,
param_dict
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
erp5_gadget
.
get
(
param_dict
.
id
);
})
.
push
(
function
(
webrtc_room
)
{
const
negotiation_list
=
JSON
.
parse
(
webrtc_room
.
negotiation_list
);
for
(
let
file_name
in
negotiation_list
)
{
if
(
negotiation_list
.
hasOwnProperty
(
file_name
)
&&
file_name
.
indexOf
(
"
offer_
"
+
param_dict
.
room
+
"
_
"
)
===
0
)
{
return
file_name
.
slice
(
6
);
}
}
return
null
;
})
.
push
(
null
,
logError
);
}
function
getErp5Content
(
erp5_gadget
,
param_dict
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
erp5_gadget
.
get
(
param_dict
.
id
);
})
.
push
(
function
(
webrtc_room
)
{
const
negotiation_list
=
JSON
.
parse
(
webrtc_room
.
negotiation_list
);
if
(
negotiation_list
[
param_dict
.
file_name
])
{
return
negotiation_list
[
param_dict
.
file_name
];
}
else
{
return
null
;
}
})
.
push
(
null
,
logError
);
}
function
putErp5Content
(
erp5_gadget
,
param_dict
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
erp5_gadget
.
get
(
param_dict
.
id
);
})
.
push
(
function
(
webrtc_room
)
{
const
negotiation_list
=
JSON
.
parse
(
webrtc_room
.
negotiation_list
);
negotiation_list
[
param_dict
.
file_name
]
=
param_dict
.
content
;
return
logQueue
(
erp5_gadget
.
put
(
param_dict
.
id
,
{
portal_type
:
"
Webrtc Room
"
,
parent_relative_url
:
"
webrtc_rooms_module
"
,
title
:
param_dict
.
room
,
negotiation_list
:
JSON
.
stringify
(
negotiation_list
),
}));
})
.
push
(
null
,
logError
);
}
function
removeErp5Content
(
erp5_gadget
,
param_dict
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
erp5_gadget
.
get
(
param_dict
.
id
);
})
.
push
(
function
(
webrtc_room
)
{
const
negotiation_list
=
JSON
.
parse
(
webrtc_room
.
negotiation_list
);
delete
negotiation_list
[
param_dict
.
file_name
];
return
logQueue
(
erp5_gadget
.
put
(
param_dict
.
id
,
{
portal_type
:
"
Webrtc Room
"
,
parent_relative_url
:
"
webrtc_rooms_module
"
,
title
:
param_dict
.
room
,
negotiation_list
:
JSON
.
stringify
(
negotiation_list
),
}));
})
.
push
(
null
,
logError
);
}
function
authenticateErp5
(
gadget
)
{
const
erp5_param_dict
=
{
gadget
:
gadget
,
gadget_name
:
"
erp5_gadget
"
,
room
:
gadget
.
state
.
login_dict
.
room
,
name
:
gadget
.
state
.
login_dict
.
name
,
file_name
:
null
,
content
:
null
,
id
:
null
,
erp5_url
:
gadget
.
state
.
erp5_url
,
function_dict
:
{
resetContent
:
resetErp5Content
,
getOffer
:
getErp5Offer
,
getContent
:
getErp5Content
,
putContent
:
putErp5Content
,
removeContent
:
removeErp5Content
,
},
};
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
createErp5Jio
(
gadget
,
erp5_param_dict
);
})
.
push
(
function
(
id
)
{
erp5_param_dict
.
id
=
id
;
return
gadget
.
getDeclaredGadget
(
"
erp5_gadget
"
);
})
.
push
(
function
(
erp5_gadget
)
{
return
resetErp5Content
(
erp5_gadget
,
erp5_param_dict
);
})
.
push
(
function
()
{
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
return
authenticateHost
(
erp5_param_dict
);
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
return
authenticateGuest
(
erp5_param_dict
);
}
})
.
push
(
null
,
logError
);
}
function
authenticateHost
(
param_dict
)
{
const
gadget
=
param_dict
.
gadget
;
let
file_name
;
let
jio_gadget
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getDeclaredGadget
(
param_dict
.
gadget_name
);
})
.
push
(
function
(
storage_gadget
)
{
jio_gadget
=
storage_gadget
;
return
pollUntilNotNull
(
gadget
,
2000
,
86400000
,
function
()
{
return
param_dict
.
function_dict
.
getOffer
(
jio_gadget
,
param_dict
);
},
function
(
offer_name
)
{
file_name
=
offer_name
;
});
})
.
push
(
function
()
{
return
pollUntilNotNull
(
gadget
,
500
,
30000
,
function
()
{
if
(
gadget
.
state
.
offer_ready
===
true
)
{
gadget
.
state
.
offer_ready
=
false
;
return
true
;
}
else
{
return
null
;
}
});
},
function
()
{
throw
param_dict
.
function_dict
.
resetContent
(
jio_gadget
,
param_dict
);
})
.
push
(
function
()
{
param_dict
.
file_name
=
"
offer_
"
+
file_name
;
return
pollUntilNotNull
(
gadget
,
500
,
10000
,
function
()
{
return
param_dict
.
function_dict
.
getContent
(
jio_gadget
,
param_dict
);
},
function
(
guest_offer
)
{
return
sendOffer
(
gadget
,
guest_offer
);
});
},
function
(
error
)
{
if
(
error
.
toString
().
indexOf
(
"
Timed out after
"
)
===
0
)
{
throw
createInitialOffer
(
gadget
);
}
else
{
throw
error
;
}
})
.
push
(
function
()
{
return
pollUntilNotNull
(
gadget
,
500
,
10000
,
function
()
{
return
gadget
.
state
.
candidate
;
},
function
(
host_answer
)
{
gadget
.
state
.
candidate
=
null
;
param_dict
.
file_name
=
"
answer_
"
+
file_name
;
param_dict
.
content
=
host_answer
;
return
param_dict
.
function_dict
.
putContent
(
jio_gadget
,
param_dict
);
});
})
.
push
(
null
,
logError
)
.
push
(
function
()
{
if
(
file_name
)
{
param_dict
.
file_name
=
"
offer_
"
+
file_name
;
return
param_dict
.
function_dict
.
removeContent
(
jio_gadget
,
param_dict
);
}
else
{
return
;
}
})
.
push
(
null
,
logError
)
.
push
(
function
()
{
return
authenticateHost
(
param_dict
);
})
}
function
authenticateGuest
(
param_dict
)
{
const
gadget
=
param_dict
.
gadget
;
let
file_name
;
let
jio_gadget
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getDeclaredGadget
(
param_dict
.
gadget_name
);
})
.
push
(
function
(
storage_gadget
)
{
jio_gadget
=
storage_gadget
;
return
pollUntilNotNull
(
gadget
,
500
,
30000
,
function
()
{
return
gadget
.
state
.
candidate
;
},
function
(
guest_offer
)
{
file_name
=
param_dict
.
room
+
"
_
"
+
param_dict
.
name
+
"
.txt
"
;
param_dict
.
file_name
=
"
offer_
"
+
file_name
;
param_dict
.
content
=
guest_offer
;
return
param_dict
.
function_dict
.
putContent
(
jio_gadget
,
param_dict
);
});
})
.
push
(
function
()
{
return
pollUntilNotNull
(
gadget
,
500
,
30000
,
function
()
{
param_dict
.
file_name
=
"
answer_
"
+
file_name
;
return
param_dict
.
function_dict
.
getContent
(
jio_gadget
,
param_dict
);
},
function
(
host_answer
)
{
return
sendAnswer
(
gadget
,
host_answer
);
});
})
.
push
(
function
()
{
// XXX pause to avoid data races, use locks later
return
RSVP
.
delay
(
1000
);
},
logError
)
.
push
(
function
()
{
if
(
file_name
)
{
param_dict
.
file_name
=
"
answer_
"
+
file_name
;
return
param_dict
.
function_dict
.
removeContent
(
jio_gadget
,
param_dict
);
}
else
{
return
;
}
})
.
push
(
null
,
logError
);
}
// WebRTC signalling functions
function
createInitialOffer
(
gadget
)
{
const
peer_list
=
gadget
.
state
.
peer_connection_list
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
new
RTCPeerConnection
(
gadget
.
state
.
ice_config
,
gadget
.
state
.
ice_constraints
);
})
.
push
(
function
(
peer_connection
)
{
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
gadget
.
element
.
querySelector
(
"
.host-offer-form textarea
"
).
value
=
""
;
styleElementByQuery
(
gadget
,
"
.host-answer-form
"
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
.host-offer-form
"
,
"
block
"
);
peer_list
.
push
(
peer_connection
);
return
setupPeerConnection
(
gadget
,
peer_connection
,
"
.host-answer-form textarea
"
);
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
peer_list
[
0
]
=
peer_connection
;
return
setupPeerConnection
(
gadget
,
peer_connection
,
"
.guest-offer-form textarea
"
);
}
})
.
push
(
function
()
{
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
peer_list
[
peer_list
.
length
-
1
].
ondatachannel
=
function
(
event
)
{
setupDataChannel
(
gadget
,
event
.
channel
);
gadget
.
state
.
data_channel_list
.
push
(
event
.
channel
);
};
return
;
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
const
peer_connection
=
peer_list
[
0
];
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
peer_connection
.
createDataChannel
(
gadget
.
state
.
login_dict
.
room
+
"
_
"
+
gadget
.
state
.
login_dict
.
name
+
"
_
"
+
gadget
.
state
.
data_channel_list
.
length
,
gadget
.
state
.
dc_constraints
);
})
.
push
(
function
(
channel
)
{
gadget
.
state
.
data_channel_list
.
push
(
channel
);
return
setupDataChannel
(
gadget
,
channel
);
})
.
push
(
function
()
{
return
peer_connection
.
createOffer
(
function
(
offer_description
)
{
peer_connection
.
setLocalDescription
(
offer_description
);
},
logError
,
gadget
.
state
.
sdp_constraints
);
})
.
push
(
null
,
logError
);
}
})
.
push
(
function
()
{
gadget
.
state
.
offer_ready
=
true
;
return
;
})
.
push
(
null
,
logError
);
}
function
setupPeerConnection
(
gadget
,
peer_connection
,
form_selector
)
{
peer_connection
.
onicecandidate
=
function
(
event
)
{
const
candidate
=
JSON
.
stringify
(
peer_connection
.
localDescription
);
gadget
.
state
.
candidate
=
candidate
;
gadget
.
element
.
querySelector
(
form_selector
).
value
=
candidate
;
};
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
peer_connection
.
oniceconnectionstatechange
=
function
()
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
peer_connection
.
iceConnectionState
===
"
connected
"
)
{
gadget
.
state
.
guest_amount
+=
1
;
}
else
if
(
peer_connection
.
iceConnectionState
===
"
disconnected
"
)
{
gadget
.
state
.
guest_amount
-=
1
;
}
return
;
})
};
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
peer_connection
.
oniceconnectionstatechange
=
function
()
{
gadget
.
element
.
querySelector
(
"
.status
"
).
textContent
=
"
WebRTC connection status:
"
+
peer_connection
.
iceConnectionState
+
"
.
"
;
if
(
peer_connection
.
iceConnectionState
===
"
failed
"
)
{
throw
webrtcError
(
gadget
,
"
WebRTC connection failed!
"
);
}
else
if
(
peer_connection
.
iceConnectionState
===
"
failed
"
)
{
throw
webrtcError
(
gadget
,
"
WebRTC connection disconnected!
"
);
}
};
}
}
function
setupDataChannel
(
gadget
,
data_channel
)
{
if
(
!
gadget
.
state
.
connected
)
{
data_channel
.
onopen
=
function
()
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
gadget
.
state
.
connected
=
true
;
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
return
createInitialOffer
(
gadget
);
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
styleElementByQuery
(
gadget
,
"
.guest-offer-form
"
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
.auth-form
"
,
"
none
"
);
return
;
}
})
.
push
(
gadget
.
state
.
dataChannelOnopen
)
.
push
(
null
,
logError
);
};
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
data_channel
.
onopen
=
function
()
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
createInitialOffer
(
gadget
);
})
.
push
(
gadget
.
state
.
dataChannelOnopen
)
.
push
(
null
,
logError
);
};
}
data_channel
.
onmessage
=
gadget
.
state
.
dataChannelOnmessage
;
}
function
sendOffer
(
gadget
,
offer
)
{
const
peer_list
=
gadget
.
state
.
peer_connection_list
;
const
peer_connection
=
peer_list
[
peer_list
.
length
-
1
];
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
new
RTCSessionDescription
(
JSON
.
parse
(
offer
));
})
.
push
(
function
(
offer_description
)
{
return
peer_connection
.
setRemoteDescription
(
offer_description
);
})
.
push
(
function
()
{
return
peer_connection
.
createAnswer
(
function
(
answer_description
)
{
peer_connection
.
setLocalDescription
(
answer_description
);
},
logError
,
gadget
.
state
.
sdp_constraints
);
})
.
push
(
null
,
logError
);
}
function
sendAnswer
(
gadget
,
answer
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
new
RTCSessionDescription
(
JSON
.
parse
(
answer
));
})
.
push
(
function
(
answer_description
)
{
return
gadget
.
state
.
peer_connection_list
[
0
]
.
setRemoteDescription
(
answer_description
);
})
.
push
(
null
,
logError
);
}
// Initialization, rendering, public methods, and event listeners
rJS
(
window
)
.
ready
(
function
(
gadget
)
{
gadget
.
state
=
{
element
:
null
,
login_dict
:
{
room
:
null
,
name
:
null
,
role
:
null
,
},
dropbox_folder
:
null
,
erp5_url
:
null
,
dataChannelOnopen
:
null
,
dataChannelOnmessage
:
null
,
candidate
:
null
,
connected
:
false
,
offer_ready
:
false
,
data_channel_list
:
[],
peer_connection_list
:
[],
guest_amount
:
0
,
archive_amount
:
0
,
ice_config
:
{
// iceServers: [{url: "stun:stun.1.google.com:19302"}],
// Must use own STUN server in production, but neither
// TURN nor STUN needed for local networks and IPv6
iceServers
:
[],
},
ice_constraints
:
{
mandatory
:
{
googIPv6
:
true
},
optional
:
[{
DtlsSrtpKeyAgreement
:
true
}],
},
dc_constraints
:
{
reliable
:
true
,
},
sdp_constraints
:
{
optional
:
[],
mandatory
:
{
OfferToReceiveAudio
:
false
,
OfferToReceiveVideo
:
false
,
},
},
};
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getElement
();
})
.
push
(
function
(
element
)
{
gadget
.
element
=
element
;
return
;
})
.
push
(
null
,
logError
);
})
// The following function is acquired from erp5_page_chat_box.js
.
declareAcquiredMethod
(
"
getContactByEmail
"
,
"
getContactByEmail
"
)
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
"
jio_put
"
,
"
jio_put
"
)
.
declareAcquiredMethod
(
"
jio_get
"
,
"
jio_get
"
)
.
declareAcquiredMethod
(
"
jio_repair
"
,
"
jio_repair
"
)
.
declareMethod
(
"
render
"
,
function
()
{
const
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
gadget
.
element
.
setAttribute
(
"
id
"
,
"
webrtc-gadget-
"
+
gadget
.
state
.
login_dict
.
room
);
styleElementByQuery
(
gadget
,
"
.host-answer-form
"
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
.guest-answer-form
"
,
"
none
"
);
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
styleElementByQuery
(
gadget
,
"
.guest-offer-form
"
,
"
none
"
);
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
styleElementByQuery
(
gadget
,
"
.host-offer-form
"
,
"
none
"
);
}
return
createInitialOffer
(
gadget
);
})
.
push
(
null
,
logError
);
})
.
declareMethod
(
"
sendMessage
"
,
function
(
message
,
source
)
{
const
gadget
=
this
;
const
message_string
=
JSON
.
stringify
(
message
);
const
channel_list
=
gadget
.
state
.
data_channel_list
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
gadget
.
state
.
login_dict
.
role
===
"
host
"
)
{
if
(
message
.
type
===
"
bundle
"
)
{
for
(
let
i
=
0
,
i_len
=
channel_list
.
length
;
i
<
i_len
;
i
++
)
{
const
channel
=
channel_list
[
i
];
if
(
channel
.
readyState
===
"
open
"
&&
channel
===
source
)
{
return
channel
.
send
(
message_string
);
}
}
}
else
{
var
promise_list
=
[];
for
(
let
i
=
0
,
i_len
=
channel_list
.
length
;
i
<
i_len
;
i
++
)
{
const
channel
=
channel_list
[
i
];
if
(
channel
.
readyState
===
"
open
"
&&
channel
!==
source
)
{
promise_list
.
push
(
channel
.
send
(
message_string
));
}
}
}
return
RSVP
.
all
(
promise_list
);
}
else
if
(
gadget
.
state
.
login_dict
.
role
===
"
guest
"
)
{
if
(
channel_list
[
0
].
readyState
!==
"
open
"
)
{
throw
"
The data channel is not open!
"
;
}
else
{
return
channel_list
[
0
].
send
(
message_string
);
}
}
})
.
push
(
null
,
logError
);
})
.
declareMethod
(
"
authenticate
"
,
function
(
auth
)
{
const
gadget
=
this
;
const
fields
=
gadget
.
element
.
querySelector
(
"
.contact-form
"
).
elements
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
gadget
.
state
.
dropbox_folder
=
fields
.
dropbox_folder
.
value
;
gadget
.
state
.
erp5_url
=
fields
.
erp5_url
.
value
;
switch
(
auth
)
{
case
"
dropbox
"
:
return
authenticateDropbox
(
gadget
);
case
"
erp5
"
:
return
authenticateErp5
(
gadget
);
default
:
return
;
}
});
})
.
declareMethod
(
"
uploadContact
"
,
function
(
event
)
{
const
gadget
=
this
;
const
fields
=
event
.
target
.
elements
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getContactByEmail
(
fields
.
email
.
value
);
})
.
push
(
function
(
id
)
{
const
param_dict
=
{
dropbox_folder
:
fields
.
dropbox_folder
.
value
,
erp5_url
:
fields
.
erp5_url
.
value
,
auth
:
gadget
.
element
.
querySelector
(
"
.auth-form
"
).
elements
.
auth
.
value
}
let
jio_configuration
=
""
;
for
(
let
key
in
param_dict
)
{
if
(
param_dict
.
hasOwnProperty
(
key
)
&&
param_dict
[
key
])
{
jio_configuration
+=
"
&
"
+
key
+
"
=
"
+
param_dict
[
key
];
}
}
return
gadget
.
jio_put
(
id
,
{
portal_type
:
"
Person
"
,
parent_relative_url
:
"
person_module
"
,
default_email_coordinate_text
:
fields
.
email
.
value
,
jio_configuration
:
jio_configuration
,
});
})
.
push
(
function
()
{
return
gadget
.
jio_repair
();
})
.
push
(
null
,
logError
);
})
.
declareMethod
(
"
downloadContact
"
,
function
()
{
const
gadget
=
this
;
const
fields
=
gadget
.
element
.
querySelector
(
"
.contact-form
"
).
elements
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getContactByEmail
(
fields
.
email
.
value
);
})
.
push
(
function
(
id
)
{
return
gadget
.
jio_get
(
id
);
})
.
push
(
function
(
contact
)
{
if
(
!
contact
)
{
throw
webrtcError
(
gadget
,
"
Email not found!
"
);
}
else
{
const
param_dict
=
{};
const
query_list
=
[[
"
email
"
,
"
e
"
],
[
"
dropbox_folder
"
,
"
d
"
],
[
"
erp5_url
"
,
"
e
"
]];
for
(
let
i
=
0
,
i_len
=
query_list
.
length
;
i
<
i_len
;
i
++
)
{
const
query
=
query_list
[
i
];
setQueryValue
(
query
,
contact
.
jio_configuration
,
fields
[
query
[
0
]]);
}
setQueryValue
([
"
auth
"
,
"
a
"
],
contact
.
jio_configuration
,
gadget
.
element
.
querySelector
(
"
.auth-form
"
).
auth
);
return
;
}
})
.
push
(
null
,
logError
);
})
.
onEvent
(
"
submit
"
,
function
(
event
)
{
const
gadget
=
this
;
switch
(
event
.
target
.
className
)
{
case
"
auth-form
"
:
return
gadget
.
authenticate
(
event
.
target
.
elements
.
auth
.
value
);
case
"
contact-form
"
:
return
gadget
.
uploadContact
(
event
);
case
"
download-form
"
:
return
gadget
.
downloadContact
();
case
"
host-offer-form
"
:
styleElementByQuery
(
gadget
,
"
.host-offer-form
"
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
.host-answer-form
"
,
"
block
"
);
return
sendOffer
(
gadget
,
resetInputValue
(
event
.
target
.
elements
.
send
));
case
"
guest-offer-form
"
:
styleElementByQuery
(
gadget
,
"
.guest-offer-form
"
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
.guest-answer-form
"
,
"
block
"
);
break
;
case
"
guest-answer-form
"
:
styleElementByQuery
(
gadget
,
"
.guest-answer-form
"
,
"
none
"
);
return
sendAnswer
(
gadget
,
resetInputValue
(
event
.
target
.
elements
.
send
));
}
});
}(
window
,
document
,
RSVP
,
rJS
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_chat_webrtc_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
<string>
contributor/person_module/1
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_chat_webrtc.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_chat_webrtc_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Chat WebRTC JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_nojquery_css.css
0 → 100644
View file @
edcd20f8
/**********************************************
* Colors
**********************************************/
/**********************************************
* Fonts (font-family)
**********************************************/
/**********************************************
* Shared
**********************************************/
/**********************************************
* http://meyerweb.com/eric/tools/css/reset/
* v2.0 | 20110126
* License: none (public domain)
**********************************************/
html
,
body
,
div
,
span
,
applet
,
object
,
iframe
,
h1
,
h2
,
h3
,
h4
,
h5
,
h6
,
p
,
blockquote
,
pre
,
a
,
abbr
,
acronym
,
address
,
big
,
cite
,
code
,
del
,
dfn
,
em
,
img
,
ins
,
kbd
,
q
,
s
,
samp
,
small
,
strike
,
strong
,
sub
,
sup
,
tt
,
var
,
b
,
u
,
i
,
center
,
dl
,
dt
,
dd
,
ol
,
ul
,
li
,
fieldset
,
form
,
label
,
legend
,
table
,
caption
,
tbody
,
tfoot
,
thead
,
tr
,
th
,
td
,
article
,
aside
,
canvas
,
details
,
embed
,
figure
,
figcaption
,
footer
,
header
,
hgroup
,
menu
,
nav
,
output
,
ruby
,
section
,
summary
,
time
,
mark
,
audio
,
video
{
margin
:
0
;
padding
:
0
;
border
:
0
;
font-size
:
100%
;
font
:
inherit
;
vertical-align
:
baseline
;
}
/* HTML5 display-role reset for older browsers */
article
,
aside
,
details
,
figcaption
,
figure
,
footer
,
header
,
hgroup
,
menu
,
nav
,
section
{
display
:
block
;
}
body
{
line-height
:
1
;
}
ol
,
ul
{
list-style
:
none
;
}
blockquote
,
q
{
quotes
:
none
;
}
blockquote
:before
,
blockquote
:after
,
q
:before
,
q
:after
{
content
:
''
;
content
:
none
;
}
table
{
border-collapse
:
collapse
;
border-spacing
:
0
;
}
/**********************************************
* Default
**********************************************/
@font-face
{
font-family
:
'FontAwesome'
;
src
:
url('font-awesome/font-awesome-webfont.eot?v=4.6.3')
;
src
:
url('font-awesome/font-awesome-webfont.eot?#iefix&v=4.6.3')
format
(
'embedded-opentype'
),
url('font-awesome/font-awesome-webfont.woff2?v=4.6.3')
format
(
'woff2'
),
url('font-awesome/font-awesome-webfont.woff?v=4.6.3')
format
(
'woff'
),
url('font-awesome/font-awesome-webfont.ttf?v=4.6.3')
format
(
'truetype'
),
url('font-awesome/font-awesome-webfont.svg?v=4.6.3#fontawesomeregular')
format
(
'svg'
);
font-weight
:
normal
;
font-style
:
normal
;
}
html
{
height
:
100%
;
width
:
100%
;
display
:
block
;
background-color
:
#FFFFFF
;
box-sizing
:
border-box
;
}
*,
*
:before
,
*
:after
{
box-sizing
:
inherit
;
}
body
{
height
:
100%
;
width
:
100%
;
display
:
block
;
color
:
#1F1F1F
;
word-wrap
:
break-word
;
}
body
,
button
,
input
,
textarea
,
select
{
font-family
:
'Open Sans'
,
Helvetica
,
Arial
,
sans-serif
;
font-size
:
12pt
;
padding
:
0
;
margin
:
0
;
font-weight
:
400
;
line-height
:
1.5
;
}
/**********************************************
* Inline elements
**********************************************/
strong
{
font-weight
:
bold
;
}
i
,
cite
,
em
,
var
,
address
,
dfn
{
font-style
:
italic
;
}
strong
,
b
{
font-weight
:
bold
;
}
u
,
ins
{
text-decoration
:
underline
;
}
s
,
strike
,
del
{
text-decoration
:
line-through
;
}
sup
{
vertical-align
:
super
;
font-size
:
smaller
;
}
sub
{
vertical-align
:
sub
;
font-size
:
smaller
;
}
small
{
font-size
:
smaller
;
}
tt
,
code
,
kbd
,
samp
{
font-family
:
"Courier New"
,
Courier
,
monospace
;
}
q
{
display
:
inline
;
quotes
:
initial
;
}
q
:before
{
content
:
open-quote
;
}
q
:after
{
content
:
close-quote
;
}
mark
{
color
:
#22CC22
;
}
/**********************************************
* Link
**********************************************/
a
{
color
:
#2FA2E4
;
text-decoration
:
none
;
}
a
[
href
=
""
]
{
color
:
#1F1F1F
;
}
a
:hover
{
text-decoration
:
underline
;
}
a
:focus
{
outline-offset
:
-2px
;
outline
:
2px
solid
#3388cc
;
}
@media
only
screen
and
(
min-width
:
90em
)
{
a
[
accesskey
]
:after
{
content
:
" ["
attr
(
accesskey
)
"] "
;
}
}
/**********************************************
* Preformatted
**********************************************/
pre
,
xmp
,
plaintext
,
listing
{
display
:
block
;
white-space
:
pre-wrap
;
}
/**********************************************
* hr
**********************************************/
hr
{
display
:
block
;
border-style
:
inset
;
border-width
:
1px
;
border-color
:
#FF6600
;
}
/**********************************************
* Text fields
**********************************************/
label
{
color
:
#777777
;
}
input
:not
([
type
=
submit
])
:not
([
type
=
file
])
:not
([
type
=
checkbox
])
:not
([
type
=
radio
])
:not
([
type
=
color
]),
textarea
,
select
{
width
:
100%
;
padding
:
3pt
;
background-color
:
#FFFFFF
;
color
:
#1F1F1F
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.3
);
border-radius
:
0.325em
;
transition
:
border
0.2s
ease-out
,
box-shadow
0.2s
ease-out
;
}
input
:not
([
type
=
submit
])
:not
([
type
=
file
])
:not
([
type
=
checkbox
])
:not
([
type
=
radio
])
:not
([
type
=
color
])
:active
,
textarea
:active
,
select
:active
,
input
:not
([
type
=
submit
])
:not
([
type
=
file
])
:not
([
type
=
checkbox
])
:not
([
type
=
radio
])
:not
([
type
=
color
])
:focus
,
textarea
:focus
,
select
:focus
{
outline
:
none
;
}
input
:not
([
type
=
submit
])
:not
([
type
=
file
])
:not
([
type
=
checkbox
])
:not
([
type
=
radio
])
:not
([
type
=
color
])
:focus
,
textarea
:focus
,
select
:focus
{
border
:
1px
solid
#3388cc
;
box-shadow
:
0
0
12pt
#3388cc
;
}
input
:not
([
type
=
submit
])
:not
([
type
=
file
])
:not
([
type
=
checkbox
])
:not
([
type
=
radio
])
:not
([
type
=
color
])
:invalid
,
textarea
:invalid
,
select
:invalid
{
border
:
1px
solid
#FF6600
;
}
input
:not
([
type
=
submit
])
:not
([
type
=
file
])
:not
([
type
=
checkbox
])
:not
([
type
=
radio
])
:not
([
type
=
color
])
:invalid:focus
,
textarea
:invalid:focus
,
select
:invalid:focus
{
box-shadow
:
0
0
12pt
#FF6600
;
}
select
{
cursor
:
pointer
;
-webkit-appearance
:
none
;
-moz-appearance
:
none
;
appearance
:
none
;
background-image
:
none
;
background-color
:
#FFFFFF
;
padding-right
:
24pt
;
background
:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='50px' height='50px'><polyline fill-opacity='0.5' points='46.139,15.518 25.166,36.49 4.193,15.519'/></svg>")
right
no-repeat
;
background-position
:
right
6pt
top
50%
;
background-size
:
12pt
12pt
;
}
select
:-moz-focusring
{
color
:
transparent
;
text-shadow
:
0
0
0
#000
;
}
textarea
{
word-wrap
:
break-word
;
white-space
:
normal
;
vertical-align
:
top
;
transition
:
height
0.2s
ease-out
;
height
:
8em
;
}
::-webkit-input-placeholder
{
color
:
#575757
;
}
:-moz-placeholder
{
/* Firefox 18- */
color
:
#575757
;
}
::-moz-placeholder
{
/* Firefox 19+ */
color
:
#575757
;
}
:-ms-input-placeholder
{
color
:
#575757
;
}
input
[
type
=
submit
],
button
{
margin
:
0
;
padding
:
0
;
border
:
none
;
background
:
transparent
;
color
:
#1F1F1F
;
/*
// XXX TODO
// transition: background-color 0.2s ease-out;
&:hover, &:focus {
outline: none;
}
&:active {
// box-shadow: inset 0 0 0 5px darken(@colorblocklinkbackground, 10%);
background-color: darken(@colorblocklinkbackground, 20%);
}
*/
cursor
:
pointer
;
}
input
[
type
=
submit
]
::-moz-focus-inner
,
button
::-moz-focus-inner
{
border
:
0px
;
padding
:
0px
;
}
input
[
type
=
submit
]
:hover
,
button
:hover
,
input
[
type
=
submit
]
:focus
,
button
:focus
{
outline
:
none
;
}
/**********************************************
* Media
**********************************************/
img
,
iframe
,
video
,
svg
{
max-width
:
100%
;
}
svg
polyline
{
stroke
:
#1F1F1F
;
fill
:
none
;
}
svg
text
{
stroke
:
#1F1F1F
;
fill
:
#1F1F1F
;
}
iframe
{
width
:
100%
;
height
:
80vh
;
background-color
:
#FFFFFF
;
}
img
{
image-orientation
:
from-image
;
}
/**********************************************
* Gadget: panel
**********************************************/
div
[
data-gadget-scope
=
'panel'
]
{
background-color
:
#444444
;
color
:
#FFFFFF
;
width
:
180pt
;
min-height
:
100%
;
max-height
:
100%
;
overflow-y
:
auto
;
position
:
fixed
;
top
:
0
;
left
:
0
;
display
:
block
;
z-index
:
2000
;
}
@media
only
screen
and
(
max-width
:
45em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'panel'
]
{
box-shadow
:
5px
0
5px
rgba
(
0
,
0
,
0
,
0.15
);
}
}
@media
only
screen
and
(
max-width
:
45em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'panel'
]
{
left
:
-186pt
;
transition
:
transform
200ms
ease-out
;
transform
:
translate3d
(
0
,
0
,
0
);
}
div
[
data-gadget-scope
=
'panel'
]
.visible
{
transform
:
translate3d
(
186pt
,
0
,
0
);
}
}
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
{
display
:
flex
;
justify-content
:
flex-start
;
}
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
.panel_img
{
text-align
:
center
;
flex
:
1
;
height
:
30pt
;
}
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
button
,
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
a
{
width
:
3em
;
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
background-color
:
#444444
;
display
:
block
;
line-height
:
30pt
;
color
:
#FFFFFF
;
}
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
button
::before
,
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
a
::before
{
float
:
left
;
text-indent
:
0
;
margin-left
:
12pt
;
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
button
[
data-i18n
=
"Close"
],
div
[
data-gadget-scope
=
'panel'
]
div
[
data-role
=
"header"
]
a
[
data-i18n
=
"Close"
]
{
display
:
none
;
}
}
div
[
data-gadget-scope
=
'panel'
]
img
{
text-align
:
left
;
height
:
100%
;
}
div
[
data-gadget-scope
=
'panel'
]
ul
li
a
{
color
:
#FFFFFF
;
padding
:
3pt
;
padding-left
:
12pt
;
display
:
block
;
text-overflow
:
ellipsis
;
overflow
:
hidden
;
white-space
:
nowrap
;
}
div
[
data-gadget-scope
=
'panel'
]
ul
li
a
::before
{
width
:
24pt
;
}
div
[
data-gadget-scope
=
'panel'
]
ul
li
a
:hover
,
div
[
data-gadget-scope
=
'panel'
]
ul
li
a
:active
{
background-color
:
#2b2b2b
;
}
/**********************************************
* Gadget: editor panel
**********************************************/
div
[
data-gadget-scope
=
'editor_panel'
]
{
background-color
:
#FFFFFF
;
width
:
180pt
;
min-height
:
100%
;
max-height
:
100%
;
overflow-y
:
auto
;
position
:
fixed
;
top
:
0
;
display
:
block
;
z-index
:
3000
;
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'editor_panel'
]
{
left
:
-186pt
;
transition
:
transform
200ms
ease-out
;
transform
:
translate3d
(
0
,
0
,
0
);
box-shadow
:
5px
0
5px
rgba
(
0
,
0
,
0
,
0.15
);
}
div
[
data-gadget-scope
=
'editor_panel'
]
.visible
{
transform
:
translate3d
(
186pt
,
0
,
0
);
}
}
@media
only
screen
and
(
max-width
:
45em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'editor_panel'
]
{
right
:
-186pt
;
transition
:
transform
200ms
ease-out
;
transform
:
translate3d
(
0
,
0
,
0
);
box-shadow
:
-5px
0
5px
rgba
(
0
,
0
,
0
,
0.15
);
}
div
[
data-gadget-scope
=
'editor_panel'
]
.visible
{
transform
:
translate3d
(
-186pt
,
0
,
0
);
}
}
div
[
data-gadget-scope
=
'editor_panel'
]
div
[
data-role
=
"header"
]
{
display
:
flex
;
justify-content
:
space-between
;
flex-direction
:
row-reverse
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
div
[
data-role
=
"header"
]
h1
{
text-align
:
left
;
line-height
:
30pt
;
max-height
:
30pt
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
div
[
data-role
=
"header"
]
button
,
div
[
data-gadget-scope
=
'editor_panel'
]
div
[
data-role
=
"header"
]
a
{
width
:
3em
;
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
display
:
block
;
line-height
:
30pt
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
div
[
data-role
=
"header"
]
button
::before
,
div
[
data-gadget-scope
=
'editor_panel'
]
div
[
data-role
=
"header"
]
a
::before
{
float
:
left
;
text-indent
:
0
;
margin-left
:
12pt
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
{
padding
:
12pt
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
fieldset
>
div
{
display
:
inline-block
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
fieldset
label
{
display
:
inline-block
;
text-align
:
center
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
fieldset
input
[
type
=
"radio"
]
{
display
:
inline-block
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
.filter_item_container
>
div
,
div
[
data-gadget-scope
=
'editor_panel'
]
section
.sort_item_container
>
div
{
display
:
flex
;
align-items
:
flex-start
;
padding
:
6pt
0
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
.filter_item_container
>
div
.filter_item
,
div
[
data-gadget-scope
=
'editor_panel'
]
section
.sort_item_container
>
div
.filter_item
,
div
[
data-gadget-scope
=
'editor_panel'
]
section
.filter_item_container
>
div
.sort_item
,
div
[
data-gadget-scope
=
'editor_panel'
]
section
.sort_item_container
>
div
.sort_item
{
flex
:
1
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
button
{
padding
:
3pt
6pt
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.14
);
border-radius
:
0.325em
;
margin-right
:
6pt
;
width
:
2em
;
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
button
:last-of-type
{
margin-right
:
0
;
}
div
[
data-gadget-scope
=
'editor_panel'
]
section
button
::before
{
margin-right
:
6pt
;
float
:
left
;
text-indent
:
0
;
}
/**********************************************
* Gadget: header
**********************************************/
div
[
data-gadget-scope
=
'header'
]
.ui-header
{
position
:
fixed
;
z-index
:
1000
;
text-align
:
center
;
display
:
flex
;
flex-flow
:
row
wrap
;
width
:
100%
;
color
:
#FFFFFF
;
background-color
:
#0E81C2
;
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
{
margin-left
:
180pt
;
}
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
a
{
color
:
#FFFFFF
;
transition
:
background-color
0.2s
ease-out
;
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
button
:hover
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
a
:hover
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
button
:active
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
a
:active
{
background-color
:
#0e90d8
;
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
{
display
:
block
;
transition
:
background-color
0.2s
ease-out
;
line-height
:
30pt
;
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
{
background-color
:
#0E81C2
;
}
}
@media
only
screen
and
(
max-width
:
45em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
{
background-color
:
#085078
;
}
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
:hover
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
:hover
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
:active
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
:active
{
background-color
:
#0e90d8
;
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
{
width
:
8em
;
}
}
@media
only
screen
and
(
max-width
:
45em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
{
width
:
3em
;
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
}
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
),
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
::before
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
::before
{
width
:
1em
;
margin-right
:
6pt
;
text-align
:
center
;
}
}
@media
only
screen
and
(
max-width
:
45em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
::before
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
::before
{
float
:
left
;
text-indent
:
0
;
margin-left
:
12pt
;
}
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
button
[
name
=
"panel"
],
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-controlgroup-controls
a
[
name
=
"panel"
]
{
display
:
none
;
}
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
>
.ui-btn-left
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
>
.ui-btn-left
a
{
border-right
:
1px
solid
rgba
(
255
,
255
,
255
,
0.55
);
}
@media
only
screen
and
(
max-width
:
45em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
>
.ui-btn-right
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
>
.ui-btn-right
a
{
border-left
:
1px
solid
rgba
(
255
,
255
,
255
,
0.55
);
}
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
>
.ui-btn-right
button
,
div
[
data-gadget-scope
=
'header'
]
.ui-header
>
.ui-btn-right
a
{
padding-left
:
24pt
;
padding-right
:
24pt
;
min-width
:
5em
;
}
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
h1
{
text-align
:
left
;
line-height
:
30pt
;
flex
:
1
;
background-color
:
#085078
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
/*
@media only screen and (min-width: 90em) {
div[data-gadget-scope='header'] .ui-header h1 {
flex: 1 100%;
}
}
div[data-gadget-scope='header'] .ui-header h1 > span {
padding-left: 42pt;
}
@media only screen and (max-width: 45em) {
div[data-gadget-scope='header'] .ui-header h1 > span {
padding-left: 30pt;
}
}
@media only screen and (min-width: 90em) {
div[data-gadget-scope='header'] .ui-header h1 > span {
padding-left: 24pt;
}
}
*/
div
[
data-gadget-scope
=
'header'
]
.ui-header
h1
a
{
display
:
block
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
h1
a
::before
{
display
:
inline-block
;
width
:
42pt
;
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
),
only
screen
and
(
max-width
:
45em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
h1
a
::before
{
text-align
:
center
;
}
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
h1
a
::before
{
padding-left
:
24pt
;
}
}
@media
only
screen
and
(
max-width
:
45em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
h1
a
::before
{
width
:
30pt
;
}
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-subheader
{
text-align
:
left
;
flex
:
1
;
}
}
@media
only
screen
and
(
max-width
:
45em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
.ui-subheader
{
flex
:
1
100%
;
}
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
{
display
:
flex
;
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
),
only
screen
and
(
max-width
:
45em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
{
flex
:
1
;
border-left
:
1px
solid
rgba
(
0
,
0
,
0
,
0.55
);
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
:first-child
{
border-left
:
none
;
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
a
{
display
:
block
;
}
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
a
{
padding-top
:
3pt
;
padding-bottom
:
3pt
;
white-space
:
nowrap
;
overflow
:
hidden
;
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
a
::before
{
font-size
:
1.2em
;
display
:
block
;
}
}
@media
only
screen
and
(
max-width
:
45em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
a
{
text-align
:
center
;
vertical-align
:
middle
;
font-size
:
1.5em
;
padding-top
:
6pt
;
padding-bottom
:
6pt
;
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
a
::before
{
float
:
left
;
text-indent
:
0
;
width
:
100%
;
}
}
@media
only
screen
and
(
min-width
:
90em
)
{
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
a
{
display
:
block
;
padding-left
:
24pt
;
padding-right
:
24pt
;
min-width
:
5em
;
line-height
:
30pt
;
white-space
:
nowrap
;
overflow
:
hidden
;
}
div
[
data-gadget-scope
=
'header'
]
.ui-header
ul
li
a
::before
{
display
:
none
;
}
}
/**********************************************
* Gadget: main
**********************************************/
.gadget-content
{
padding
:
24pt
;
padding-top
:
66pt
;
/*
@media @smartphone {
.ui-field-contain {
// padding: 0.8em 0;
// make sure there is a bottom border
// XXX TODO: border should be visible only if not input
// XXX How to not show it on last field?
border-bottom: 1px solid rgba(0, 0, 0, 0.15);
}
}
*/
/* form validation (assuming label>span is used) */
}
.gadget-content
div
[
data-gadget-scope
=
'm'
]
{
animation
:
fadein
0.2s
ease-out
;
}
.gadget-content
input
[
type
=
'submit'
]
{
padding
:
6pt
;
background-color
:
#444444
;
color
:
#FFFFFF
;
border-radius
:
0.325em
;
border-width
:
1px
;
border-style
:
solid
;
min-width
:
8em
;
}
.gadget-content
input
[
type
=
'submit'
]
:hover
,
.gadget-content
input
[
type
=
'submit'
]
:focus
{
background-color
:
#5e5e5e
;
}
.gadget-content
input
[
type
=
'submit'
]
:active
{
background-color
:
#777777
;
}
@media
only
screen
and
(
min-width
:
90em
)
{
.gadget-content
{
margin-left
:
180pt
;
}
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
.gadget-content
{
padding-top
:
7em
;
}
}
@media
only
screen
and
(
max-width
:
45em
)
{
.gadget-content
{
padding
:
6pt
;
padding-top
:
6em
;
}
}
.gadget-content
.ui-field-contain
{
padding
:
6pt
0
;
}
.gadget-content
.ui-field-contain
div
{
width
:
100%
;
}
.gadget-content
.ui-content-header-plain
{
font-size
:
150%
;
}
.gadget-content
ul
.document-listview
:not
(
:last-of-type
)
{
margin-bottom
:
12pt
;
}
.gadget-content
ul
.document-listview
:first-child
{
margin-top
:
6pt
;
}
.gadget-content
ul
.document-listview
li
{
border-color
:
rgba
(
0
,
0
,
0
,
0.3
);
border-width
:
1px
;
border-style
:
solid
;
border-bottom-style
:
none
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.15
);
}
.gadget-content
ul
.document-listview
li
:not
(
.ui-li-has-count
)
a
:after
{
font-family
:
FontAwesome
;
content
:
"\f0da"
;
text-align
:
right
;
float
:
right
;
position
:
absolute
;
right
:
12pt
;
}
.gadget-content
ul
.document-listview
li
a
{
display
:
block
;
position
:
relative
;
padding
:
6pt
12pt
;
padding-right
:
24pt
;
color
:
#222222
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
.gadget-content
ul
.document-listview
li
a
:hover
,
.gadget-content
ul
.document-listview
li
a
:active
{
background-color
:
#e0e0e0
;
}
.gadget-content
ul
.document-listview
li
:first-child
{
border-top-left-radius
:
0.325em
;
border-top-right-radius
:
0.325em
;
}
.gadget-content
ul
.document-listview
li
:last-child
{
border-bottom-left-radius
:
0.325em
;
border-bottom-right-radius
:
0.325em
;
border-bottom-style
:
solid
;
}
.gadget-content
ul
.document-listview
li
.ui-li-count
{
float
:
right
;
padding
:
0
6pt
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.14
);
border-radius
:
0.325em
;
position
:
absolute
;
right
:
6pt
;
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
),
only
screen
and
(
min-width
:
90em
)
{
.gadget-content
.left
,
.gadget-content
.right
{
vertical-align
:
top
;
display
:
inline-block
;
width
:
50%
;
}
.gadget-content
.right
{
padding-left
:
24pt
;
}
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
),
only
screen
and
(
min-width
:
90em
)
{
.gadget-content
.ui-field-contain
{
display
:
flex
;
}
.gadget-content
.ui-field-contain
label
{
flex
:
1
;
}
.gadget-content
.ui-field-contain
label
+
div
{
flex
:
3
;
}
}
@media
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
),
only
screen
and
(
min-width
:
90em
)
{
.gadget-content
.center
.ui-field-contain
label
+
div
{
flex
:
7
;
}
}
.gadget-content
form
label
{
position
:
relative
;
}
.gadget-content
form
label
span
span
{
animation
:
fadein
0.2s
ease-out
;
}
@media
only
screen
and
(
min-width
:
90em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
.gadget-content
form
label
span
span
{
background-color
:
#FF6600
;
color
:
#f8fff3
;
left
:
110%
;
position
:
absolute
;
bottom
:
130%
;
white-space
:
pre
;
padding
:
6pt
;
border-radius
:
0.325em
;
width
:
auto
;
z-index
:
1001
;
}
.gadget-content
form
label
span
span
:before
{
position
:
absolute
;
top
:
100%
;
left
:
2em
;
display
:
inline-block
;
border-right
:
6pt
solid
transparent
;
border-top
:
6pt
solid
#FF6600
;
border-left
:
6pt
solid
transparent
;
content
:
''
;
}
}
@media
only
screen
and
(
max-width
:
45em
)
{
.gadget-content
form
label
span
span
{
margin-left
:
6pt
;
color
:
#FF6600
;
}
}
/**********************************************
* Gadget: relation field
**********************************************/
.relation-input
{
display
:
flex
;
}
.relation-input
a
{
width
:
24pt
;
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
display
:
block
;
padding-top
:
3pt
;
padding-bottom
:
3pt
;
}
.relation-input
a
::before
{
float
:
left
;
text-indent
:
0
;
margin-left
:
6pt
;
}
.relation-input
div
{
position
:
relative
;
}
.relation-input
ul
{
position
:
absolute
;
display
:
block
;
width
:
100%
;
z-index
:
501
;
}
.relation-input
ul
li
{
cursor
:
pointer
;
background-color
:
#444444
;
color
:
#FFFFFF
;
padding
:
3pt
;
padding-left
:
6pt
;
display
:
block
;
text-overflow
:
ellipsis
;
overflow
:
hidden
;
white-space
:
nowrap
;
}
.relation-input
ul
li
::before
{
padding-right
:
6pt
;
}
.relation-input
ul
li
:hover
,
.relation-input
ul
li
:active
{
background-color
:
#2b2b2b
;
}
/**********************************************
* Gadget: datetime field
**********************************************/
.datetimefield
{
display
:
flex
;
}
.datetimefield
div
[
data-gadget-scope
=
INPUT
]
{
flex
:
2
;
}
.datetimefield
div
[
data-gadget-scope
=
SELECT
]
{
flex
:
1
;
}
/**********************************************
* Gadget: float/integer field
**********************************************/
.floatfield
p
,
.integerfield
p
,
.floatfield
input
,
.integerfield
input
{
text-align
:
right
;
}
/**********************************************
* Listbox
**********************************************/
div
[
data-gadget-scope
=
'erp5_searchfield'
]
{
padding-top
:
6pt
;
}
div
[
data-gadget-scope
=
'erp5_searchfield'
]
.ui-input-text
{
display
:
flex
;
}
div
[
data-gadget-scope
=
'erp5_searchfield'
]
.ui-input-text
div
[
data-gadget-scope
=
'input'
]
{
width
:
100%
;
}
div
[
data-gadget-scope
=
'erp5_searchfield'
]
button
{
padding
:
3pt
;
}
.document-table
.ui-table-header
{
display
:
flex
;
padding-bottom
:
6pt
;
}
@media
only
screen
and
(
max-width
:
45em
)
{
.document-table
.ui-table-header
{
border-bottom
:
2px
solid
rgba
(
0
,
0
,
0
,
0.14902
);
}
}
.document-table
.ui-table-header
h1
{
color
:
#777777
;
flex
:
2
;
align-self
:
flex-end
;
}
.document-table
.ui-table-header
button
{
padding
:
3pt
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.14
);
border-radius
:
0.325em
;
margin-right
:
6pt
;
}
.document-table
.ui-table-header
button
:last-of-type
{
margin-right
:
0
;
}
@media
only
screen
and
(
max-width
:
45em
)
{
.document-table
.ui-table-header
button
{
width
:
2em
;
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
}
}
.document-table
.ui-table-header
button
::before
{
margin-right
:
6pt
;
}
@media
only
screen
and
(
max-width
:
45em
)
{
.document-table
.ui-table-header
button
::before
{
float
:
left
;
text-indent
:
0
;
}
}
.document-table
table
{
width
:
100%
;
text-align
:
left
;
}
.document-table
table
th
,
.document-table
table
td
{
vertical-align
:
middle
;
}
.document-table
table
thead
{
background-color
:
#0E81C2
;
color
:
#FFFFFF
;
}
.document-table
table
thead
tr
th
{
padding
:
6pt
3pt
;
}
@media
only
screen
and
(
max-width
:
45em
)
{
.document-table
table
thead
{
display
:
none
;
}
}
.document-table
table
a
{
color
:
#1F1F1F
;
}
.document-table
table
tbody
{
animation
:
fadein
0.2s
ease-out
;
}
.document-table
table
tbody
tr
:nth-child
(
even
)
{
background-color
:
#f2f2f2
;
}
.document-table
table
tbody
tr
:hover
,
.document-table
table
tbody
tr
:active
{
background-color
:
#e0e0e0
;
}
@media
only
screen
and
(
min-width
:
90em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
90em
)
{
.document-table
table
tbody
a
{
display
:
block
;
padding
:
3pt
;
}
}
@media
only
screen
and
(
max-width
:
45em
)
{
.document-table
table
tbody
tr
{
display
:
block
;
overflow
:
hidden
;
width
:
100%
;
height
:
4em
;
position
:
relative
;
}
.document-table
table
tbody
tr
td
,
.document-table
table
tbody
tr
th
{
display
:
inline-block
;
}
.document-table
table
tbody
tr
td
:first-child
,
.document-table
table
tbody
tr
th
:first-child
{
display
:
inline-block
;
width
:
100%
;
}
.document-table
table
tbody
tr
td
:first-child
a
,
.document-table
table
tbody
tr
th
:first-child
a
{
position
:
absolute
;
width
:
100%
;
top
:
0
;
bottom
:
0
;
overflow
:
hidden
;
white-space
:
nowrap
;
text-overflow
:
ellipsis
;
}
.document-table
table
tbody
tr
td
:first-child
a
:after
,
.document-table
table
tbody
tr
th
:first-child
a
:after
{
font-family
:
FontAwesome
;
content
:
"\f0da"
;
font-size
:
1.25em
;
position
:
absolute
;
right
:
6pt
;
top
:
50%
;
margin-top
:
-0.75em
;
background-color
:
#FFFFFF
;
border-radius
:
0.5em
;
width
:
1em
;
text-align
:
center
;
line-height
:
1em
;
}
.document-table
table
tbody
tr
td
:first-child
~
th
,
.document-table
table
tbody
tr
th
:first-child
~
th
,
.document-table
table
tbody
tr
td
:first-child
~
td
,
.document-table
table
tbody
tr
th
:first-child
~
td
{
font-size
:
0.8em
;
}
.document-table
table
tbody
tr
td
:first-child
~
th
a
,
.document-table
table
tbody
tr
th
:first-child
~
th
a
,
.document-table
table
tbody
tr
td
:first-child
~
td
a
,
.document-table
table
tbody
tr
th
:first-child
~
td
a
{
pointer-events
:
none
;
}
.document-table
table
tbody
tr
td
:first-child
~
th
:not
(
:last-child
)
a
:not
(
:empty
)
:after
,
.document-table
table
tbody
tr
th
:first-child
~
th
:not
(
:last-child
)
a
:not
(
:empty
)
:after
,
.document-table
table
tbody
tr
td
:first-child
~
td
:not
(
:last-child
)
a
:not
(
:empty
)
:after
,
.document-table
table
tbody
tr
th
:first-child
~
td
:not
(
:last-child
)
a
:not
(
:empty
)
:after
{
content
:
" ~ "
;
}
}
.document-table
table
tfoot
.ui-controlgroup-controls
{
display
:
flex
;
padding-top
:
6pt
;
border-top
:
2px
solid
rgba
(
0
,
0
,
0
,
0.14902
);
}
.document-table
table
tfoot
.ui-controlgroup-controls
span
{
opacity
:
.3
;
flex
:
2
;
text-align
:
right
;
}
.document-table
table
tfoot
.ui-controlgroup-controls
a
{
padding
:
6pt
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.14
);
border-radius
:
0.325em
;
margin-right
:
6pt
;
}
.document-table
table
tfoot
.ui-controlgroup-controls
a
:last-of-type
{
margin-right
:
0
;
}
.document-table
table
tfoot
.ui-controlgroup-controls
a
:hover
,
.document-table
table
tfoot
.ui-controlgroup-controls
a
:active
{
background-color
:
#e0e0e0
;
}
@media
only
screen
and
(
max-width
:
45em
)
{
.document-table
table
tfoot
.ui-controlgroup-controls
a
{
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
}
}
.document-table
table
tfoot
.ui-controlgroup-controls
a
::before
{
margin-right
:
6pt
;
}
@media
only
screen
and
(
max-width
:
45em
)
{
.document-table
table
tfoot
.ui-controlgroup-controls
a
::before
{
float
:
left
;
text-indent
:
6pt
;
}
}
/**********************************************
* Notification
**********************************************/
div
[
data-gadget-scope
=
'notification'
]
{
position
:
fixed
;
z-index
:
99999
;
bottom
:
12pt
;
right
:
-192pt
;
transition
:
transform
200ms
ease-out
;
transform
:
translate3d
(
0
,
0
,
0
);
}
div
[
data-gadget-scope
=
'notification'
]
.visible
{
transform
:
translate3d
(
-216pt
,
0
,
0
);
}
@media
only
screen
and
(
max-width
:
45em
)
{
div
[
data-gadget-scope
=
'notification'
]
.visible
{
transform
:
translate3d
(
-198pt
,
0
,
0
);
}
}
div
[
data-gadget-scope
=
'notification'
]
button
{
text-align
:
left
;
width
:
180pt
;
padding
:
12pt
;
background-color
:
#FF6600
;
color
:
#f8fff3
;
border-radius
:
0.325em
;
}
/**********************************************
* JQM
**********************************************/
.ui-disabled
{
opacity
:
.3
;
cursor
:
default
;
pointer-events
:
none
;
}
.ui-screen-hidden
{
display
:
none
;
}
/**********************************************
* First loader
**********************************************/
.first-loader
{
position
:
fixed
;
left
:
50%
;
top
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
font-size
:
300%
;
}
/**********************************************
* Keyframes
**********************************************/
@keyframes
spin
{
from
{
transform
:
rotate
(
0deg
);
}
to
{
transform
:
rotate
(
360deg
);
}
}
@keyframes
fadein
{
from
{
opacity
:
0
;
}
to
{
opacity
:
1
;
}
}
/*!
* Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
@font-face
{
font-family
:
'FontAwesome'
;
font-weight
:
normal
;
font-style
:
normal
;
}
.fa
{
display
:
inline-block
;
font
:
normal
normal
normal
14px
/
1
FontAwesome
;
font-size
:
inherit
;
text-rendering
:
auto
;
-webkit-font-smoothing
:
antialiased
;
-moz-osx-font-smoothing
:
grayscale
;
}
/* makes the font 33% larger relative to the icon container */
.fa-lg
{
font-size
:
1.33333333em
;
line-height
:
0.75em
;
vertical-align
:
-15%
;
}
.fa-2x
{
font-size
:
2em
;
}
.fa-3x
{
font-size
:
3em
;
}
.fa-4x
{
font-size
:
4em
;
}
.fa-5x
{
font-size
:
5em
;
}
.fa-fw
{
width
:
1.28571429em
;
text-align
:
center
;
}
.fa-ul
{
padding-left
:
0
;
margin-left
:
2.14285714em
;
list-style-type
:
none
;
}
.fa-ul
>
li
{
position
:
relative
;
}
.fa-li
{
position
:
absolute
;
left
:
-2.14285714em
;
width
:
2.14285714em
;
top
:
0.14285714em
;
text-align
:
center
;
}
.fa-li.fa-lg
{
left
:
-1.85714286em
;
}
.fa-border
{
padding
:
.2em
.25em
.15em
;
border
:
solid
0.08em
#eeeeee
;
border-radius
:
.1em
;
}
.fa-pull-left
{
float
:
left
;
}
.fa-pull-right
{
float
:
right
;
}
.fa.fa-pull-left
{
margin-right
:
.3em
;
}
.fa.fa-pull-right
{
margin-left
:
.3em
;
}
/* Deprecated as of 4.4.0 */
.pull-right
{
float
:
right
;
}
.pull-left
{
float
:
left
;
}
.fa.pull-left
{
margin-right
:
.3em
;
}
.fa.pull-right
{
margin-left
:
.3em
;
}
.fa-spin
{
-webkit-animation
:
fa-spin
2s
infinite
linear
;
animation
:
fa-spin
2s
infinite
linear
;
}
.fa-pulse
{
-webkit-animation
:
fa-spin
1s
infinite
steps
(
8
);
animation
:
fa-spin
1s
infinite
steps
(
8
);
}
@-webkit-keyframes
fa-spin
{
0
%
{
-webkit-transform
:
rotate
(
0deg
);
transform
:
rotate
(
0deg
);
}
100
%
{
-webkit-transform
:
rotate
(
359deg
);
transform
:
rotate
(
359deg
);
}
}
@keyframes
fa-spin
{
0
%
{
-webkit-transform
:
rotate
(
0deg
);
transform
:
rotate
(
0deg
);
}
100
%
{
-webkit-transform
:
rotate
(
359deg
);
transform
:
rotate
(
359deg
);
}
}
.fa-rotate-90
{
-ms-filter
:
"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"
;
-webkit-transform
:
rotate
(
90deg
);
-ms-transform
:
rotate
(
90deg
);
transform
:
rotate
(
90deg
);
}
.fa-rotate-180
{
-ms-filter
:
"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"
;
-webkit-transform
:
rotate
(
180deg
);
-ms-transform
:
rotate
(
180deg
);
transform
:
rotate
(
180deg
);
}
.fa-rotate-270
{
-ms-filter
:
"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"
;
-webkit-transform
:
rotate
(
270deg
);
-ms-transform
:
rotate
(
270deg
);
transform
:
rotate
(
270deg
);
}
.fa-flip-horizontal
{
-ms-filter
:
"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"
;
-webkit-transform
:
scale
(
-1
,
1
);
-ms-transform
:
scale
(
-1
,
1
);
transform
:
scale
(
-1
,
1
);
}
.fa-flip-vertical
{
-ms-filter
:
"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"
;
-webkit-transform
:
scale
(
1
,
-1
);
-ms-transform
:
scale
(
1
,
-1
);
transform
:
scale
(
1
,
-1
);
}
:root
.fa-rotate-90
,
:root
.fa-rotate-180
,
:root
.fa-rotate-270
,
:root
.fa-flip-horizontal
,
:root
.fa-flip-vertical
{
filter
:
none
;
}
.fa-stack
{
position
:
relative
;
display
:
inline-block
;
width
:
2em
;
height
:
2em
;
line-height
:
2em
;
vertical-align
:
middle
;
}
.fa-stack-1x
,
.fa-stack-2x
{
position
:
absolute
;
left
:
0
;
width
:
100%
;
text-align
:
center
;
}
.fa-stack-1x
{
line-height
:
inherit
;
}
.fa-stack-2x
{
font-size
:
2em
;
}
.fa-inverse
{
color
:
#ffffff
;
}
/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
readers do not read off random characters that represent icons */
.fa-glass
:before
{
content
:
"\f000"
;
}
.fa-music
:before
{
content
:
"\f001"
;
}
.fa-search
:before
{
content
:
"\f002"
;
}
.fa-envelope-o
:before
{
content
:
"\f003"
;
}
.fa-heart
:before
{
content
:
"\f004"
;
}
.fa-star
:before
{
content
:
"\f005"
;
}
.fa-star-o
:before
{
content
:
"\f006"
;
}
.fa-user
:before
{
content
:
"\f007"
;
}
.fa-film
:before
{
content
:
"\f008"
;
}
.fa-th-large
:before
{
content
:
"\f009"
;
}
.fa-th
:before
{
content
:
"\f00a"
;
}
.fa-th-list
:before
{
content
:
"\f00b"
;
}
.fa-check
:before
{
content
:
"\f00c"
;
}
.fa-remove
:before
,
.fa-close
:before
,
.fa-times
:before
{
content
:
"\f00d"
;
}
.fa-search-plus
:before
{
content
:
"\f00e"
;
}
.fa-search-minus
:before
{
content
:
"\f010"
;
}
.fa-power-off
:before
{
content
:
"\f011"
;
}
.fa-signal
:before
{
content
:
"\f012"
;
}
.fa-gear
:before
,
.fa-cog
:before
{
content
:
"\f013"
;
}
.fa-trash-o
:before
{
content
:
"\f014"
;
}
.fa-home
:before
{
content
:
"\f015"
;
}
.fa-file-o
:before
{
content
:
"\f016"
;
}
.fa-clock-o
:before
{
content
:
"\f017"
;
}
.fa-road
:before
{
content
:
"\f018"
;
}
.fa-download
:before
{
content
:
"\f019"
;
}
.fa-arrow-circle-o-down
:before
{
content
:
"\f01a"
;
}
.fa-arrow-circle-o-up
:before
{
content
:
"\f01b"
;
}
.fa-inbox
:before
{
content
:
"\f01c"
;
}
.fa-play-circle-o
:before
{
content
:
"\f01d"
;
}
.fa-rotate-right
:before
,
.fa-repeat
:before
{
content
:
"\f01e"
;
}
.fa-refresh
:before
{
content
:
"\f021"
;
}
.fa-list-alt
:before
{
content
:
"\f022"
;
}
.fa-lock
:before
{
content
:
"\f023"
;
}
.fa-flag
:before
{
content
:
"\f024"
;
}
.fa-headphones
:before
{
content
:
"\f025"
;
}
.fa-volume-off
:before
{
content
:
"\f026"
;
}
.fa-volume-down
:before
{
content
:
"\f027"
;
}
.fa-volume-up
:before
{
content
:
"\f028"
;
}
.fa-qrcode
:before
{
content
:
"\f029"
;
}
.fa-barcode
:before
{
content
:
"\f02a"
;
}
.fa-tag
:before
{
content
:
"\f02b"
;
}
.fa-tags
:before
{
content
:
"\f02c"
;
}
.fa-book
:before
{
content
:
"\f02d"
;
}
.fa-bookmark
:before
{
content
:
"\f02e"
;
}
.fa-print
:before
{
content
:
"\f02f"
;
}
.fa-camera
:before
{
content
:
"\f030"
;
}
.fa-font
:before
{
content
:
"\f031"
;
}
.fa-bold
:before
{
content
:
"\f032"
;
}
.fa-italic
:before
{
content
:
"\f033"
;
}
.fa-text-height
:before
{
content
:
"\f034"
;
}
.fa-text-width
:before
{
content
:
"\f035"
;
}
.fa-align-left
:before
{
content
:
"\f036"
;
}
.fa-align-center
:before
{
content
:
"\f037"
;
}
.fa-align-right
:before
{
content
:
"\f038"
;
}
.fa-align-justify
:before
{
content
:
"\f039"
;
}
.fa-list
:before
{
content
:
"\f03a"
;
}
.fa-dedent
:before
,
.fa-outdent
:before
{
content
:
"\f03b"
;
}
.fa-indent
:before
{
content
:
"\f03c"
;
}
.fa-video-camera
:before
{
content
:
"\f03d"
;
}
.fa-photo
:before
,
.fa-image
:before
,
.fa-picture-o
:before
{
content
:
"\f03e"
;
}
.fa-pencil
:before
{
content
:
"\f040"
;
}
.fa-map-marker
:before
{
content
:
"\f041"
;
}
.fa-adjust
:before
{
content
:
"\f042"
;
}
.fa-tint
:before
{
content
:
"\f043"
;
}
.fa-edit
:before
,
.fa-pencil-square-o
:before
{
content
:
"\f044"
;
}
.fa-share-square-o
:before
{
content
:
"\f045"
;
}
.fa-check-square-o
:before
{
content
:
"\f046"
;
}
.fa-arrows
:before
{
content
:
"\f047"
;
}
.fa-step-backward
:before
{
content
:
"\f048"
;
}
.fa-fast-backward
:before
{
content
:
"\f049"
;
}
.fa-backward
:before
{
content
:
"\f04a"
;
}
.fa-play
:before
{
content
:
"\f04b"
;
}
.fa-pause
:before
{
content
:
"\f04c"
;
}
.fa-stop
:before
{
content
:
"\f04d"
;
}
.fa-forward
:before
{
content
:
"\f04e"
;
}
.fa-fast-forward
:before
{
content
:
"\f050"
;
}
.fa-step-forward
:before
{
content
:
"\f051"
;
}
.fa-eject
:before
{
content
:
"\f052"
;
}
.fa-chevron-left
:before
{
content
:
"\f053"
;
}
.fa-chevron-right
:before
{
content
:
"\f054"
;
}
.fa-plus-circle
:before
{
content
:
"\f055"
;
}
.fa-minus-circle
:before
{
content
:
"\f056"
;
}
.fa-times-circle
:before
{
content
:
"\f057"
;
}
.fa-check-circle
:before
{
content
:
"\f058"
;
}
.fa-question-circle
:before
{
content
:
"\f059"
;
}
.fa-info-circle
:before
{
content
:
"\f05a"
;
}
.fa-crosshairs
:before
{
content
:
"\f05b"
;
}
.fa-times-circle-o
:before
{
content
:
"\f05c"
;
}
.fa-check-circle-o
:before
{
content
:
"\f05d"
;
}
.fa-ban
:before
{
content
:
"\f05e"
;
}
.fa-arrow-left
:before
{
content
:
"\f060"
;
}
.fa-arrow-right
:before
{
content
:
"\f061"
;
}
.fa-arrow-up
:before
{
content
:
"\f062"
;
}
.fa-arrow-down
:before
{
content
:
"\f063"
;
}
.fa-mail-forward
:before
,
.fa-share
:before
{
content
:
"\f064"
;
}
.fa-expand
:before
{
content
:
"\f065"
;
}
.fa-compress
:before
{
content
:
"\f066"
;
}
.fa-plus
:before
{
content
:
"\f067"
;
}
.fa-minus
:before
{
content
:
"\f068"
;
}
.fa-asterisk
:before
{
content
:
"\f069"
;
}
.fa-exclamation-circle
:before
{
content
:
"\f06a"
;
}
.fa-gift
:before
{
content
:
"\f06b"
;
}
.fa-leaf
:before
{
content
:
"\f06c"
;
}
.fa-fire
:before
{
content
:
"\f06d"
;
}
.fa-eye
:before
{
content
:
"\f06e"
;
}
.fa-eye-slash
:before
{
content
:
"\f070"
;
}
.fa-warning
:before
,
.fa-exclamation-triangle
:before
{
content
:
"\f071"
;
}
.fa-plane
:before
{
content
:
"\f072"
;
}
.fa-calendar
:before
{
content
:
"\f073"
;
}
.fa-random
:before
{
content
:
"\f074"
;
}
.fa-comment
:before
{
content
:
"\f075"
;
}
.fa-magnet
:before
{
content
:
"\f076"
;
}
.fa-chevron-up
:before
{
content
:
"\f077"
;
}
.fa-chevron-down
:before
{
content
:
"\f078"
;
}
.fa-retweet
:before
{
content
:
"\f079"
;
}
.fa-shopping-cart
:before
{
content
:
"\f07a"
;
}
.fa-folder
:before
{
content
:
"\f07b"
;
}
.fa-folder-open
:before
{
content
:
"\f07c"
;
}
.fa-arrows-v
:before
{
content
:
"\f07d"
;
}
.fa-arrows-h
:before
{
content
:
"\f07e"
;
}
.fa-bar-chart-o
:before
,
.fa-bar-chart
:before
{
content
:
"\f080"
;
}
.fa-twitter-square
:before
{
content
:
"\f081"
;
}
.fa-facebook-square
:before
{
content
:
"\f082"
;
}
.fa-camera-retro
:before
{
content
:
"\f083"
;
}
.fa-key
:before
{
content
:
"\f084"
;
}
.fa-gears
:before
,
.fa-cogs
:before
{
content
:
"\f085"
;
}
.fa-comments
:before
{
content
:
"\f086"
;
}
.fa-thumbs-o-up
:before
{
content
:
"\f087"
;
}
.fa-thumbs-o-down
:before
{
content
:
"\f088"
;
}
.fa-star-half
:before
{
content
:
"\f089"
;
}
.fa-heart-o
:before
{
content
:
"\f08a"
;
}
.fa-sign-out
:before
{
content
:
"\f08b"
;
}
.fa-linkedin-square
:before
{
content
:
"\f08c"
;
}
.fa-thumb-tack
:before
{
content
:
"\f08d"
;
}
.fa-external-link
:before
{
content
:
"\f08e"
;
}
.fa-sign-in
:before
{
content
:
"\f090"
;
}
.fa-trophy
:before
{
content
:
"\f091"
;
}
.fa-github-square
:before
{
content
:
"\f092"
;
}
.fa-upload
:before
{
content
:
"\f093"
;
}
.fa-lemon-o
:before
{
content
:
"\f094"
;
}
.fa-phone
:before
{
content
:
"\f095"
;
}
.fa-square-o
:before
{
content
:
"\f096"
;
}
.fa-bookmark-o
:before
{
content
:
"\f097"
;
}
.fa-phone-square
:before
{
content
:
"\f098"
;
}
.fa-twitter
:before
{
content
:
"\f099"
;
}
.fa-facebook-f
:before
,
.fa-facebook
:before
{
content
:
"\f09a"
;
}
.fa-github
:before
{
content
:
"\f09b"
;
}
.fa-unlock
:before
{
content
:
"\f09c"
;
}
.fa-credit-card
:before
{
content
:
"\f09d"
;
}
.fa-feed
:before
,
.fa-rss
:before
{
content
:
"\f09e"
;
}
.fa-hdd-o
:before
{
content
:
"\f0a0"
;
}
.fa-bullhorn
:before
{
content
:
"\f0a1"
;
}
.fa-bell
:before
{
content
:
"\f0f3"
;
}
.fa-certificate
:before
{
content
:
"\f0a3"
;
}
.fa-hand-o-right
:before
{
content
:
"\f0a4"
;
}
.fa-hand-o-left
:before
{
content
:
"\f0a5"
;
}
.fa-hand-o-up
:before
{
content
:
"\f0a6"
;
}
.fa-hand-o-down
:before
{
content
:
"\f0a7"
;
}
.fa-arrow-circle-left
:before
{
content
:
"\f0a8"
;
}
.fa-arrow-circle-right
:before
{
content
:
"\f0a9"
;
}
.fa-arrow-circle-up
:before
{
content
:
"\f0aa"
;
}
.fa-arrow-circle-down
:before
{
content
:
"\f0ab"
;
}
.fa-globe
:before
{
content
:
"\f0ac"
;
}
.fa-wrench
:before
{
content
:
"\f0ad"
;
}
.fa-tasks
:before
{
content
:
"\f0ae"
;
}
.fa-filter
:before
{
content
:
"\f0b0"
;
}
.fa-briefcase
:before
{
content
:
"\f0b1"
;
}
.fa-arrows-alt
:before
{
content
:
"\f0b2"
;
}
.fa-group
:before
,
.fa-users
:before
{
content
:
"\f0c0"
;
}
.fa-chain
:before
,
.fa-link
:before
{
content
:
"\f0c1"
;
}
.fa-cloud
:before
{
content
:
"\f0c2"
;
}
.fa-flask
:before
{
content
:
"\f0c3"
;
}
.fa-cut
:before
,
.fa-scissors
:before
{
content
:
"\f0c4"
;
}
.fa-copy
:before
,
.fa-files-o
:before
{
content
:
"\f0c5"
;
}
.fa-paperclip
:before
{
content
:
"\f0c6"
;
}
.fa-save
:before
,
.fa-floppy-o
:before
{
content
:
"\f0c7"
;
}
.fa-square
:before
{
content
:
"\f0c8"
;
}
.fa-navicon
:before
,
.fa-reorder
:before
,
.fa-bars
:before
{
content
:
"\f0c9"
;
}
.fa-list-ul
:before
{
content
:
"\f0ca"
;
}
.fa-list-ol
:before
{
content
:
"\f0cb"
;
}
.fa-strikethrough
:before
{
content
:
"\f0cc"
;
}
.fa-underline
:before
{
content
:
"\f0cd"
;
}
.fa-table
:before
{
content
:
"\f0ce"
;
}
.fa-magic
:before
{
content
:
"\f0d0"
;
}
.fa-truck
:before
{
content
:
"\f0d1"
;
}
.fa-pinterest
:before
{
content
:
"\f0d2"
;
}
.fa-pinterest-square
:before
{
content
:
"\f0d3"
;
}
.fa-google-plus-square
:before
{
content
:
"\f0d4"
;
}
.fa-google-plus
:before
{
content
:
"\f0d5"
;
}
.fa-money
:before
{
content
:
"\f0d6"
;
}
.fa-caret-down
:before
{
content
:
"\f0d7"
;
}
.fa-caret-up
:before
{
content
:
"\f0d8"
;
}
.fa-caret-left
:before
{
content
:
"\f0d9"
;
}
.fa-caret-right
:before
{
content
:
"\f0da"
;
}
.fa-columns
:before
{
content
:
"\f0db"
;
}
.fa-unsorted
:before
,
.fa-sort
:before
{
content
:
"\f0dc"
;
}
.fa-sort-down
:before
,
.fa-sort-desc
:before
{
content
:
"\f0dd"
;
}
.fa-sort-up
:before
,
.fa-sort-asc
:before
{
content
:
"\f0de"
;
}
.fa-envelope
:before
{
content
:
"\f0e0"
;
}
.fa-linkedin
:before
{
content
:
"\f0e1"
;
}
.fa-rotate-left
:before
,
.fa-undo
:before
{
content
:
"\f0e2"
;
}
.fa-legal
:before
,
.fa-gavel
:before
{
content
:
"\f0e3"
;
}
.fa-dashboard
:before
,
.fa-tachometer
:before
{
content
:
"\f0e4"
;
}
.fa-comment-o
:before
{
content
:
"\f0e5"
;
}
.fa-comments-o
:before
{
content
:
"\f0e6"
;
}
.fa-flash
:before
,
.fa-bolt
:before
{
content
:
"\f0e7"
;
}
.fa-sitemap
:before
{
content
:
"\f0e8"
;
}
.fa-umbrella
:before
{
content
:
"\f0e9"
;
}
.fa-paste
:before
,
.fa-clipboard
:before
{
content
:
"\f0ea"
;
}
.fa-lightbulb-o
:before
{
content
:
"\f0eb"
;
}
.fa-exchange
:before
{
content
:
"\f0ec"
;
}
.fa-cloud-download
:before
{
content
:
"\f0ed"
;
}
.fa-cloud-upload
:before
{
content
:
"\f0ee"
;
}
.fa-user-md
:before
{
content
:
"\f0f0"
;
}
.fa-stethoscope
:before
{
content
:
"\f0f1"
;
}
.fa-suitcase
:before
{
content
:
"\f0f2"
;
}
.fa-bell-o
:before
{
content
:
"\f0a2"
;
}
.fa-coffee
:before
{
content
:
"\f0f4"
;
}
.fa-cutlery
:before
{
content
:
"\f0f5"
;
}
.fa-file-text-o
:before
{
content
:
"\f0f6"
;
}
.fa-building-o
:before
{
content
:
"\f0f7"
;
}
.fa-hospital-o
:before
{
content
:
"\f0f8"
;
}
.fa-ambulance
:before
{
content
:
"\f0f9"
;
}
.fa-medkit
:before
{
content
:
"\f0fa"
;
}
.fa-fighter-jet
:before
{
content
:
"\f0fb"
;
}
.fa-beer
:before
{
content
:
"\f0fc"
;
}
.fa-h-square
:before
{
content
:
"\f0fd"
;
}
.fa-plus-square
:before
{
content
:
"\f0fe"
;
}
.fa-angle-double-left
:before
{
content
:
"\f100"
;
}
.fa-angle-double-right
:before
{
content
:
"\f101"
;
}
.fa-angle-double-up
:before
{
content
:
"\f102"
;
}
.fa-angle-double-down
:before
{
content
:
"\f103"
;
}
.fa-angle-left
:before
{
content
:
"\f104"
;
}
.fa-angle-right
:before
{
content
:
"\f105"
;
}
.fa-angle-up
:before
{
content
:
"\f106"
;
}
.fa-angle-down
:before
{
content
:
"\f107"
;
}
.fa-desktop
:before
{
content
:
"\f108"
;
}
.fa-laptop
:before
{
content
:
"\f109"
;
}
.fa-tablet
:before
{
content
:
"\f10a"
;
}
.fa-mobile-phone
:before
,
.fa-mobile
:before
{
content
:
"\f10b"
;
}
.fa-circle-o
:before
{
content
:
"\f10c"
;
}
.fa-quote-left
:before
{
content
:
"\f10d"
;
}
.fa-quote-right
:before
{
content
:
"\f10e"
;
}
.fa-spinner
:before
{
content
:
"\f110"
;
}
.fa-circle
:before
{
content
:
"\f111"
;
}
.fa-mail-reply
:before
,
.fa-reply
:before
{
content
:
"\f112"
;
}
.fa-github-alt
:before
{
content
:
"\f113"
;
}
.fa-folder-o
:before
{
content
:
"\f114"
;
}
.fa-folder-open-o
:before
{
content
:
"\f115"
;
}
.fa-smile-o
:before
{
content
:
"\f118"
;
}
.fa-frown-o
:before
{
content
:
"\f119"
;
}
.fa-meh-o
:before
{
content
:
"\f11a"
;
}
.fa-gamepad
:before
{
content
:
"\f11b"
;
}
.fa-keyboard-o
:before
{
content
:
"\f11c"
;
}
.fa-flag-o
:before
{
content
:
"\f11d"
;
}
.fa-flag-checkered
:before
{
content
:
"\f11e"
;
}
.fa-terminal
:before
{
content
:
"\f120"
;
}
.fa-code
:before
{
content
:
"\f121"
;
}
.fa-mail-reply-all
:before
,
.fa-reply-all
:before
{
content
:
"\f122"
;
}
.fa-star-half-empty
:before
,
.fa-star-half-full
:before
,
.fa-star-half-o
:before
{
content
:
"\f123"
;
}
.fa-location-arrow
:before
{
content
:
"\f124"
;
}
.fa-crop
:before
{
content
:
"\f125"
;
}
.fa-code-fork
:before
{
content
:
"\f126"
;
}
.fa-unlink
:before
,
.fa-chain-broken
:before
{
content
:
"\f127"
;
}
.fa-question
:before
{
content
:
"\f128"
;
}
.fa-info
:before
{
content
:
"\f129"
;
}
.fa-exclamation
:before
{
content
:
"\f12a"
;
}
.fa-superscript
:before
{
content
:
"\f12b"
;
}
.fa-subscript
:before
{
content
:
"\f12c"
;
}
.fa-eraser
:before
{
content
:
"\f12d"
;
}
.fa-puzzle-piece
:before
{
content
:
"\f12e"
;
}
.fa-microphone
:before
{
content
:
"\f130"
;
}
.fa-microphone-slash
:before
{
content
:
"\f131"
;
}
.fa-shield
:before
{
content
:
"\f132"
;
}
.fa-calendar-o
:before
{
content
:
"\f133"
;
}
.fa-fire-extinguisher
:before
{
content
:
"\f134"
;
}
.fa-rocket
:before
{
content
:
"\f135"
;
}
.fa-maxcdn
:before
{
content
:
"\f136"
;
}
.fa-chevron-circle-left
:before
{
content
:
"\f137"
;
}
.fa-chevron-circle-right
:before
{
content
:
"\f138"
;
}
.fa-chevron-circle-up
:before
{
content
:
"\f139"
;
}
.fa-chevron-circle-down
:before
{
content
:
"\f13a"
;
}
.fa-html5
:before
{
content
:
"\f13b"
;
}
.fa-css3
:before
{
content
:
"\f13c"
;
}
.fa-anchor
:before
{
content
:
"\f13d"
;
}
.fa-unlock-alt
:before
{
content
:
"\f13e"
;
}
.fa-bullseye
:before
{
content
:
"\f140"
;
}
.fa-ellipsis-h
:before
{
content
:
"\f141"
;
}
.fa-ellipsis-v
:before
{
content
:
"\f142"
;
}
.fa-rss-square
:before
{
content
:
"\f143"
;
}
.fa-play-circle
:before
{
content
:
"\f144"
;
}
.fa-ticket
:before
{
content
:
"\f145"
;
}
.fa-minus-square
:before
{
content
:
"\f146"
;
}
.fa-minus-square-o
:before
{
content
:
"\f147"
;
}
.fa-level-up
:before
{
content
:
"\f148"
;
}
.fa-level-down
:before
{
content
:
"\f149"
;
}
.fa-check-square
:before
{
content
:
"\f14a"
;
}
.fa-pencil-square
:before
{
content
:
"\f14b"
;
}
.fa-external-link-square
:before
{
content
:
"\f14c"
;
}
.fa-share-square
:before
{
content
:
"\f14d"
;
}
.fa-compass
:before
{
content
:
"\f14e"
;
}
.fa-toggle-down
:before
,
.fa-caret-square-o-down
:before
{
content
:
"\f150"
;
}
.fa-toggle-up
:before
,
.fa-caret-square-o-up
:before
{
content
:
"\f151"
;
}
.fa-toggle-right
:before
,
.fa-caret-square-o-right
:before
{
content
:
"\f152"
;
}
.fa-euro
:before
,
.fa-eur
:before
{
content
:
"\f153"
;
}
.fa-gbp
:before
{
content
:
"\f154"
;
}
.fa-dollar
:before
,
.fa-usd
:before
{
content
:
"\f155"
;
}
.fa-rupee
:before
,
.fa-inr
:before
{
content
:
"\f156"
;
}
.fa-cny
:before
,
.fa-rmb
:before
,
.fa-yen
:before
,
.fa-jpy
:before
{
content
:
"\f157"
;
}
.fa-ruble
:before
,
.fa-rouble
:before
,
.fa-rub
:before
{
content
:
"\f158"
;
}
.fa-won
:before
,
.fa-krw
:before
{
content
:
"\f159"
;
}
.fa-bitcoin
:before
,
.fa-btc
:before
{
content
:
"\f15a"
;
}
.fa-file
:before
{
content
:
"\f15b"
;
}
.fa-file-text
:before
{
content
:
"\f15c"
;
}
.fa-sort-alpha-asc
:before
{
content
:
"\f15d"
;
}
.fa-sort-alpha-desc
:before
{
content
:
"\f15e"
;
}
.fa-sort-amount-asc
:before
{
content
:
"\f160"
;
}
.fa-sort-amount-desc
:before
{
content
:
"\f161"
;
}
.fa-sort-numeric-asc
:before
{
content
:
"\f162"
;
}
.fa-sort-numeric-desc
:before
{
content
:
"\f163"
;
}
.fa-thumbs-up
:before
{
content
:
"\f164"
;
}
.fa-thumbs-down
:before
{
content
:
"\f165"
;
}
.fa-youtube-square
:before
{
content
:
"\f166"
;
}
.fa-youtube
:before
{
content
:
"\f167"
;
}
.fa-xing
:before
{
content
:
"\f168"
;
}
.fa-xing-square
:before
{
content
:
"\f169"
;
}
.fa-youtube-play
:before
{
content
:
"\f16a"
;
}
.fa-dropbox
:before
{
content
:
"\f16b"
;
}
.fa-stack-overflow
:before
{
content
:
"\f16c"
;
}
.fa-instagram
:before
{
content
:
"\f16d"
;
}
.fa-flickr
:before
{
content
:
"\f16e"
;
}
.fa-adn
:before
{
content
:
"\f170"
;
}
.fa-bitbucket
:before
{
content
:
"\f171"
;
}
.fa-bitbucket-square
:before
{
content
:
"\f172"
;
}
.fa-tumblr
:before
{
content
:
"\f173"
;
}
.fa-tumblr-square
:before
{
content
:
"\f174"
;
}
.fa-long-arrow-down
:before
{
content
:
"\f175"
;
}
.fa-long-arrow-up
:before
{
content
:
"\f176"
;
}
.fa-long-arrow-left
:before
{
content
:
"\f177"
;
}
.fa-long-arrow-right
:before
{
content
:
"\f178"
;
}
.fa-apple
:before
{
content
:
"\f179"
;
}
.fa-windows
:before
{
content
:
"\f17a"
;
}
.fa-android
:before
{
content
:
"\f17b"
;
}
.fa-linux
:before
{
content
:
"\f17c"
;
}
.fa-dribbble
:before
{
content
:
"\f17d"
;
}
.fa-skype
:before
{
content
:
"\f17e"
;
}
.fa-foursquare
:before
{
content
:
"\f180"
;
}
.fa-trello
:before
{
content
:
"\f181"
;
}
.fa-female
:before
{
content
:
"\f182"
;
}
.fa-male
:before
{
content
:
"\f183"
;
}
.fa-gittip
:before
,
.fa-gratipay
:before
{
content
:
"\f184"
;
}
.fa-sun-o
:before
{
content
:
"\f185"
;
}
.fa-moon-o
:before
{
content
:
"\f186"
;
}
.fa-archive
:before
{
content
:
"\f187"
;
}
.fa-bug
:before
{
content
:
"\f188"
;
}
.fa-vk
:before
{
content
:
"\f189"
;
}
.fa-weibo
:before
{
content
:
"\f18a"
;
}
.fa-renren
:before
{
content
:
"\f18b"
;
}
.fa-pagelines
:before
{
content
:
"\f18c"
;
}
.fa-stack-exchange
:before
{
content
:
"\f18d"
;
}
.fa-arrow-circle-o-right
:before
{
content
:
"\f18e"
;
}
.fa-arrow-circle-o-left
:before
{
content
:
"\f190"
;
}
.fa-toggle-left
:before
,
.fa-caret-square-o-left
:before
{
content
:
"\f191"
;
}
.fa-dot-circle-o
:before
{
content
:
"\f192"
;
}
.fa-wheelchair
:before
{
content
:
"\f193"
;
}
.fa-vimeo-square
:before
{
content
:
"\f194"
;
}
.fa-turkish-lira
:before
,
.fa-try
:before
{
content
:
"\f195"
;
}
.fa-plus-square-o
:before
{
content
:
"\f196"
;
}
.fa-space-shuttle
:before
{
content
:
"\f197"
;
}
.fa-slack
:before
{
content
:
"\f198"
;
}
.fa-envelope-square
:before
{
content
:
"\f199"
;
}
.fa-wordpress
:before
{
content
:
"\f19a"
;
}
.fa-openid
:before
{
content
:
"\f19b"
;
}
.fa-institution
:before
,
.fa-bank
:before
,
.fa-university
:before
{
content
:
"\f19c"
;
}
.fa-mortar-board
:before
,
.fa-graduation-cap
:before
{
content
:
"\f19d"
;
}
.fa-yahoo
:before
{
content
:
"\f19e"
;
}
.fa-google
:before
{
content
:
"\f1a0"
;
}
.fa-reddit
:before
{
content
:
"\f1a1"
;
}
.fa-reddit-square
:before
{
content
:
"\f1a2"
;
}
.fa-stumbleupon-circle
:before
{
content
:
"\f1a3"
;
}
.fa-stumbleupon
:before
{
content
:
"\f1a4"
;
}
.fa-delicious
:before
{
content
:
"\f1a5"
;
}
.fa-digg
:before
{
content
:
"\f1a6"
;
}
.fa-pied-piper-pp
:before
{
content
:
"\f1a7"
;
}
.fa-pied-piper-alt
:before
{
content
:
"\f1a8"
;
}
.fa-drupal
:before
{
content
:
"\f1a9"
;
}
.fa-joomla
:before
{
content
:
"\f1aa"
;
}
.fa-language
:before
{
content
:
"\f1ab"
;
}
.fa-fax
:before
{
content
:
"\f1ac"
;
}
.fa-building
:before
{
content
:
"\f1ad"
;
}
.fa-child
:before
{
content
:
"\f1ae"
;
}
.fa-paw
:before
{
content
:
"\f1b0"
;
}
.fa-spoon
:before
{
content
:
"\f1b1"
;
}
.fa-cube
:before
{
content
:
"\f1b2"
;
}
.fa-cubes
:before
{
content
:
"\f1b3"
;
}
.fa-behance
:before
{
content
:
"\f1b4"
;
}
.fa-behance-square
:before
{
content
:
"\f1b5"
;
}
.fa-steam
:before
{
content
:
"\f1b6"
;
}
.fa-steam-square
:before
{
content
:
"\f1b7"
;
}
.fa-recycle
:before
{
content
:
"\f1b8"
;
}
.fa-automobile
:before
,
.fa-car
:before
{
content
:
"\f1b9"
;
}
.fa-cab
:before
,
.fa-taxi
:before
{
content
:
"\f1ba"
;
}
.fa-tree
:before
{
content
:
"\f1bb"
;
}
.fa-spotify
:before
{
content
:
"\f1bc"
;
}
.fa-deviantart
:before
{
content
:
"\f1bd"
;
}
.fa-soundcloud
:before
{
content
:
"\f1be"
;
}
.fa-database
:before
{
content
:
"\f1c0"
;
}
.fa-file-pdf-o
:before
{
content
:
"\f1c1"
;
}
.fa-file-word-o
:before
{
content
:
"\f1c2"
;
}
.fa-file-excel-o
:before
{
content
:
"\f1c3"
;
}
.fa-file-powerpoint-o
:before
{
content
:
"\f1c4"
;
}
.fa-file-photo-o
:before
,
.fa-file-picture-o
:before
,
.fa-file-image-o
:before
{
content
:
"\f1c5"
;
}
.fa-file-zip-o
:before
,
.fa-file-archive-o
:before
{
content
:
"\f1c6"
;
}
.fa-file-sound-o
:before
,
.fa-file-audio-o
:before
{
content
:
"\f1c7"
;
}
.fa-file-movie-o
:before
,
.fa-file-video-o
:before
{
content
:
"\f1c8"
;
}
.fa-file-code-o
:before
{
content
:
"\f1c9"
;
}
.fa-vine
:before
{
content
:
"\f1ca"
;
}
.fa-codepen
:before
{
content
:
"\f1cb"
;
}
.fa-jsfiddle
:before
{
content
:
"\f1cc"
;
}
.fa-life-bouy
:before
,
.fa-life-buoy
:before
,
.fa-life-saver
:before
,
.fa-support
:before
,
.fa-life-ring
:before
{
content
:
"\f1cd"
;
}
.fa-circle-o-notch
:before
{
content
:
"\f1ce"
;
}
.fa-ra
:before
,
.fa-resistance
:before
,
.fa-rebel
:before
{
content
:
"\f1d0"
;
}
.fa-ge
:before
,
.fa-empire
:before
{
content
:
"\f1d1"
;
}
.fa-git-square
:before
{
content
:
"\f1d2"
;
}
.fa-git
:before
{
content
:
"\f1d3"
;
}
.fa-y-combinator-square
:before
,
.fa-yc-square
:before
,
.fa-hacker-news
:before
{
content
:
"\f1d4"
;
}
.fa-tencent-weibo
:before
{
content
:
"\f1d5"
;
}
.fa-qq
:before
{
content
:
"\f1d6"
;
}
.fa-wechat
:before
,
.fa-weixin
:before
{
content
:
"\f1d7"
;
}
.fa-send
:before
,
.fa-paper-plane
:before
{
content
:
"\f1d8"
;
}
.fa-send-o
:before
,
.fa-paper-plane-o
:before
{
content
:
"\f1d9"
;
}
.fa-history
:before
{
content
:
"\f1da"
;
}
.fa-circle-thin
:before
{
content
:
"\f1db"
;
}
.fa-header
:before
{
content
:
"\f1dc"
;
}
.fa-paragraph
:before
{
content
:
"\f1dd"
;
}
.fa-sliders
:before
{
content
:
"\f1de"
;
}
.fa-share-alt
:before
{
content
:
"\f1e0"
;
}
.fa-share-alt-square
:before
{
content
:
"\f1e1"
;
}
.fa-bomb
:before
{
content
:
"\f1e2"
;
}
.fa-soccer-ball-o
:before
,
.fa-futbol-o
:before
{
content
:
"\f1e3"
;
}
.fa-tty
:before
{
content
:
"\f1e4"
;
}
.fa-binoculars
:before
{
content
:
"\f1e5"
;
}
.fa-plug
:before
{
content
:
"\f1e6"
;
}
.fa-slideshare
:before
{
content
:
"\f1e7"
;
}
.fa-twitch
:before
{
content
:
"\f1e8"
;
}
.fa-yelp
:before
{
content
:
"\f1e9"
;
}
.fa-newspaper-o
:before
{
content
:
"\f1ea"
;
}
.fa-wifi
:before
{
content
:
"\f1eb"
;
}
.fa-calculator
:before
{
content
:
"\f1ec"
;
}
.fa-paypal
:before
{
content
:
"\f1ed"
;
}
.fa-google-wallet
:before
{
content
:
"\f1ee"
;
}
.fa-cc-visa
:before
{
content
:
"\f1f0"
;
}
.fa-cc-mastercard
:before
{
content
:
"\f1f1"
;
}
.fa-cc-discover
:before
{
content
:
"\f1f2"
;
}
.fa-cc-amex
:before
{
content
:
"\f1f3"
;
}
.fa-cc-paypal
:before
{
content
:
"\f1f4"
;
}
.fa-cc-stripe
:before
{
content
:
"\f1f5"
;
}
.fa-bell-slash
:before
{
content
:
"\f1f6"
;
}
.fa-bell-slash-o
:before
{
content
:
"\f1f7"
;
}
.fa-trash
:before
{
content
:
"\f1f8"
;
}
.fa-copyright
:before
{
content
:
"\f1f9"
;
}
.fa-at
:before
{
content
:
"\f1fa"
;
}
.fa-eyedropper
:before
{
content
:
"\f1fb"
;
}
.fa-paint-brush
:before
{
content
:
"\f1fc"
;
}
.fa-birthday-cake
:before
{
content
:
"\f1fd"
;
}
.fa-area-chart
:before
{
content
:
"\f1fe"
;
}
.fa-pie-chart
:before
{
content
:
"\f200"
;
}
.fa-line-chart
:before
{
content
:
"\f201"
;
}
.fa-lastfm
:before
{
content
:
"\f202"
;
}
.fa-lastfm-square
:before
{
content
:
"\f203"
;
}
.fa-toggle-off
:before
{
content
:
"\f204"
;
}
.fa-toggle-on
:before
{
content
:
"\f205"
;
}
.fa-bicycle
:before
{
content
:
"\f206"
;
}
.fa-bus
:before
{
content
:
"\f207"
;
}
.fa-ioxhost
:before
{
content
:
"\f208"
;
}
.fa-angellist
:before
{
content
:
"\f209"
;
}
.fa-cc
:before
{
content
:
"\f20a"
;
}
.fa-shekel
:before
,
.fa-sheqel
:before
,
.fa-ils
:before
{
content
:
"\f20b"
;
}
.fa-meanpath
:before
{
content
:
"\f20c"
;
}
.fa-buysellads
:before
{
content
:
"\f20d"
;
}
.fa-connectdevelop
:before
{
content
:
"\f20e"
;
}
.fa-dashcube
:before
{
content
:
"\f210"
;
}
.fa-forumbee
:before
{
content
:
"\f211"
;
}
.fa-leanpub
:before
{
content
:
"\f212"
;
}
.fa-sellsy
:before
{
content
:
"\f213"
;
}
.fa-shirtsinbulk
:before
{
content
:
"\f214"
;
}
.fa-simplybuilt
:before
{
content
:
"\f215"
;
}
.fa-skyatlas
:before
{
content
:
"\f216"
;
}
.fa-cart-plus
:before
{
content
:
"\f217"
;
}
.fa-cart-arrow-down
:before
{
content
:
"\f218"
;
}
.fa-diamond
:before
{
content
:
"\f219"
;
}
.fa-ship
:before
{
content
:
"\f21a"
;
}
.fa-user-secret
:before
{
content
:
"\f21b"
;
}
.fa-motorcycle
:before
{
content
:
"\f21c"
;
}
.fa-street-view
:before
{
content
:
"\f21d"
;
}
.fa-heartbeat
:before
{
content
:
"\f21e"
;
}
.fa-venus
:before
{
content
:
"\f221"
;
}
.fa-mars
:before
{
content
:
"\f222"
;
}
.fa-mercury
:before
{
content
:
"\f223"
;
}
.fa-intersex
:before
,
.fa-transgender
:before
{
content
:
"\f224"
;
}
.fa-transgender-alt
:before
{
content
:
"\f225"
;
}
.fa-venus-double
:before
{
content
:
"\f226"
;
}
.fa-mars-double
:before
{
content
:
"\f227"
;
}
.fa-venus-mars
:before
{
content
:
"\f228"
;
}
.fa-mars-stroke
:before
{
content
:
"\f229"
;
}
.fa-mars-stroke-v
:before
{
content
:
"\f22a"
;
}
.fa-mars-stroke-h
:before
{
content
:
"\f22b"
;
}
.fa-neuter
:before
{
content
:
"\f22c"
;
}
.fa-genderless
:before
{
content
:
"\f22d"
;
}
.fa-facebook-official
:before
{
content
:
"\f230"
;
}
.fa-pinterest-p
:before
{
content
:
"\f231"
;
}
.fa-whatsapp
:before
{
content
:
"\f232"
;
}
.fa-server
:before
{
content
:
"\f233"
;
}
.fa-user-plus
:before
{
content
:
"\f234"
;
}
.fa-user-times
:before
{
content
:
"\f235"
;
}
.fa-hotel
:before
,
.fa-bed
:before
{
content
:
"\f236"
;
}
.fa-viacoin
:before
{
content
:
"\f237"
;
}
.fa-train
:before
{
content
:
"\f238"
;
}
.fa-subway
:before
{
content
:
"\f239"
;
}
.fa-medium
:before
{
content
:
"\f23a"
;
}
.fa-yc
:before
,
.fa-y-combinator
:before
{
content
:
"\f23b"
;
}
.fa-optin-monster
:before
{
content
:
"\f23c"
;
}
.fa-opencart
:before
{
content
:
"\f23d"
;
}
.fa-expeditedssl
:before
{
content
:
"\f23e"
;
}
.fa-battery-4
:before
,
.fa-battery
:before
,
.fa-battery-full
:before
{
content
:
"\f240"
;
}
.fa-battery-3
:before
,
.fa-battery-three-quarters
:before
{
content
:
"\f241"
;
}
.fa-battery-2
:before
,
.fa-battery-half
:before
{
content
:
"\f242"
;
}
.fa-battery-1
:before
,
.fa-battery-quarter
:before
{
content
:
"\f243"
;
}
.fa-battery-0
:before
,
.fa-battery-empty
:before
{
content
:
"\f244"
;
}
.fa-mouse-pointer
:before
{
content
:
"\f245"
;
}
.fa-i-cursor
:before
{
content
:
"\f246"
;
}
.fa-object-group
:before
{
content
:
"\f247"
;
}
.fa-object-ungroup
:before
{
content
:
"\f248"
;
}
.fa-sticky-note
:before
{
content
:
"\f249"
;
}
.fa-sticky-note-o
:before
{
content
:
"\f24a"
;
}
.fa-cc-jcb
:before
{
content
:
"\f24b"
;
}
.fa-cc-diners-club
:before
{
content
:
"\f24c"
;
}
.fa-clone
:before
{
content
:
"\f24d"
;
}
.fa-balance-scale
:before
{
content
:
"\f24e"
;
}
.fa-hourglass-o
:before
{
content
:
"\f250"
;
}
.fa-hourglass-1
:before
,
.fa-hourglass-start
:before
{
content
:
"\f251"
;
}
.fa-hourglass-2
:before
,
.fa-hourglass-half
:before
{
content
:
"\f252"
;
}
.fa-hourglass-3
:before
,
.fa-hourglass-end
:before
{
content
:
"\f253"
;
}
.fa-hourglass
:before
{
content
:
"\f254"
;
}
.fa-hand-grab-o
:before
,
.fa-hand-rock-o
:before
{
content
:
"\f255"
;
}
.fa-hand-stop-o
:before
,
.fa-hand-paper-o
:before
{
content
:
"\f256"
;
}
.fa-hand-scissors-o
:before
{
content
:
"\f257"
;
}
.fa-hand-lizard-o
:before
{
content
:
"\f258"
;
}
.fa-hand-spock-o
:before
{
content
:
"\f259"
;
}
.fa-hand-pointer-o
:before
{
content
:
"\f25a"
;
}
.fa-hand-peace-o
:before
{
content
:
"\f25b"
;
}
.fa-trademark
:before
{
content
:
"\f25c"
;
}
.fa-registered
:before
{
content
:
"\f25d"
;
}
.fa-creative-commons
:before
{
content
:
"\f25e"
;
}
.fa-gg
:before
{
content
:
"\f260"
;
}
.fa-gg-circle
:before
{
content
:
"\f261"
;
}
.fa-tripadvisor
:before
{
content
:
"\f262"
;
}
.fa-odnoklassniki
:before
{
content
:
"\f263"
;
}
.fa-odnoklassniki-square
:before
{
content
:
"\f264"
;
}
.fa-get-pocket
:before
{
content
:
"\f265"
;
}
.fa-wikipedia-w
:before
{
content
:
"\f266"
;
}
.fa-safari
:before
{
content
:
"\f267"
;
}
.fa-chrome
:before
{
content
:
"\f268"
;
}
.fa-firefox
:before
{
content
:
"\f269"
;
}
.fa-opera
:before
{
content
:
"\f26a"
;
}
.fa-internet-explorer
:before
{
content
:
"\f26b"
;
}
.fa-tv
:before
,
.fa-television
:before
{
content
:
"\f26c"
;
}
.fa-contao
:before
{
content
:
"\f26d"
;
}
.fa-500px
:before
{
content
:
"\f26e"
;
}
.fa-amazon
:before
{
content
:
"\f270"
;
}
.fa-calendar-plus-o
:before
{
content
:
"\f271"
;
}
.fa-calendar-minus-o
:before
{
content
:
"\f272"
;
}
.fa-calendar-times-o
:before
{
content
:
"\f273"
;
}
.fa-calendar-check-o
:before
{
content
:
"\f274"
;
}
.fa-industry
:before
{
content
:
"\f275"
;
}
.fa-map-pin
:before
{
content
:
"\f276"
;
}
.fa-map-signs
:before
{
content
:
"\f277"
;
}
.fa-map-o
:before
{
content
:
"\f278"
;
}
.fa-map
:before
{
content
:
"\f279"
;
}
.fa-commenting
:before
{
content
:
"\f27a"
;
}
.fa-commenting-o
:before
{
content
:
"\f27b"
;
}
.fa-houzz
:before
{
content
:
"\f27c"
;
}
.fa-vimeo
:before
{
content
:
"\f27d"
;
}
.fa-black-tie
:before
{
content
:
"\f27e"
;
}
.fa-fonticons
:before
{
content
:
"\f280"
;
}
.fa-reddit-alien
:before
{
content
:
"\f281"
;
}
.fa-edge
:before
{
content
:
"\f282"
;
}
.fa-credit-card-alt
:before
{
content
:
"\f283"
;
}
.fa-codiepie
:before
{
content
:
"\f284"
;
}
.fa-modx
:before
{
content
:
"\f285"
;
}
.fa-fort-awesome
:before
{
content
:
"\f286"
;
}
.fa-usb
:before
{
content
:
"\f287"
;
}
.fa-product-hunt
:before
{
content
:
"\f288"
;
}
.fa-mixcloud
:before
{
content
:
"\f289"
;
}
.fa-scribd
:before
{
content
:
"\f28a"
;
}
.fa-pause-circle
:before
{
content
:
"\f28b"
;
}
.fa-pause-circle-o
:before
{
content
:
"\f28c"
;
}
.fa-stop-circle
:before
{
content
:
"\f28d"
;
}
.fa-stop-circle-o
:before
{
content
:
"\f28e"
;
}
.fa-shopping-bag
:before
{
content
:
"\f290"
;
}
.fa-shopping-basket
:before
{
content
:
"\f291"
;
}
.fa-hashtag
:before
{
content
:
"\f292"
;
}
.fa-bluetooth
:before
{
content
:
"\f293"
;
}
.fa-bluetooth-b
:before
{
content
:
"\f294"
;
}
.fa-percent
:before
{
content
:
"\f295"
;
}
.fa-gitlab
:before
{
content
:
"\f296"
;
}
.fa-wpbeginner
:before
{
content
:
"\f297"
;
}
.fa-wpforms
:before
{
content
:
"\f298"
;
}
.fa-envira
:before
{
content
:
"\f299"
;
}
.fa-universal-access
:before
{
content
:
"\f29a"
;
}
.fa-wheelchair-alt
:before
{
content
:
"\f29b"
;
}
.fa-question-circle-o
:before
{
content
:
"\f29c"
;
}
.fa-blind
:before
{
content
:
"\f29d"
;
}
.fa-audio-description
:before
{
content
:
"\f29e"
;
}
.fa-volume-control-phone
:before
{
content
:
"\f2a0"
;
}
.fa-braille
:before
{
content
:
"\f2a1"
;
}
.fa-assistive-listening-systems
:before
{
content
:
"\f2a2"
;
}
.fa-asl-interpreting
:before
,
.fa-american-sign-language-interpreting
:before
{
content
:
"\f2a3"
;
}
.fa-deafness
:before
,
.fa-hard-of-hearing
:before
,
.fa-deaf
:before
{
content
:
"\f2a4"
;
}
.fa-glide
:before
{
content
:
"\f2a5"
;
}
.fa-glide-g
:before
{
content
:
"\f2a6"
;
}
.fa-signing
:before
,
.fa-sign-language
:before
{
content
:
"\f2a7"
;
}
.fa-low-vision
:before
{
content
:
"\f2a8"
;
}
.fa-viadeo
:before
{
content
:
"\f2a9"
;
}
.fa-viadeo-square
:before
{
content
:
"\f2aa"
;
}
.fa-snapchat
:before
{
content
:
"\f2ab"
;
}
.fa-snapchat-ghost
:before
{
content
:
"\f2ac"
;
}
.fa-snapchat-square
:before
{
content
:
"\f2ad"
;
}
.fa-pied-piper
:before
{
content
:
"\f2ae"
;
}
.fa-first-order
:before
{
content
:
"\f2b0"
;
}
.fa-yoast
:before
{
content
:
"\f2b1"
;
}
.fa-themeisle
:before
{
content
:
"\f2b2"
;
}
.fa-google-plus-circle
:before
,
.fa-google-plus-official
:before
{
content
:
"\f2b3"
;
}
.fa-fa
:before
,
.fa-font-awesome
:before
{
content
:
"\f2b4"
;
}
.fa-handshake-o
:before
{
content
:
"\f2b5"
;
}
.fa-envelope-open
:before
{
content
:
"\f2b6"
;
}
.fa-envelope-open-o
:before
{
content
:
"\f2b7"
;
}
.fa-linode
:before
{
content
:
"\f2b8"
;
}
.fa-address-book
:before
{
content
:
"\f2b9"
;
}
.fa-address-book-o
:before
{
content
:
"\f2ba"
;
}
.fa-vcard
:before
,
.fa-address-card
:before
{
content
:
"\f2bb"
;
}
.fa-vcard-o
:before
,
.fa-address-card-o
:before
{
content
:
"\f2bc"
;
}
.fa-user-circle
:before
{
content
:
"\f2bd"
;
}
.fa-user-circle-o
:before
{
content
:
"\f2be"
;
}
.fa-user-o
:before
{
content
:
"\f2c0"
;
}
.fa-id-badge
:before
{
content
:
"\f2c1"
;
}
.fa-drivers-license
:before
,
.fa-id-card
:before
{
content
:
"\f2c2"
;
}
.fa-drivers-license-o
:before
,
.fa-id-card-o
:before
{
content
:
"\f2c3"
;
}
.fa-quora
:before
{
content
:
"\f2c4"
;
}
.fa-free-code-camp
:before
{
content
:
"\f2c5"
;
}
.fa-telegram
:before
{
content
:
"\f2c6"
;
}
.fa-thermometer-4
:before
,
.fa-thermometer
:before
,
.fa-thermometer-full
:before
{
content
:
"\f2c7"
;
}
.fa-thermometer-3
:before
,
.fa-thermometer-three-quarters
:before
{
content
:
"\f2c8"
;
}
.fa-thermometer-2
:before
,
.fa-thermometer-half
:before
{
content
:
"\f2c9"
;
}
.fa-thermometer-1
:before
,
.fa-thermometer-quarter
:before
{
content
:
"\f2ca"
;
}
.fa-thermometer-0
:before
,
.fa-thermometer-empty
:before
{
content
:
"\f2cb"
;
}
.fa-shower
:before
{
content
:
"\f2cc"
;
}
.fa-bathtub
:before
,
.fa-s15
:before
,
.fa-bath
:before
{
content
:
"\f2cd"
;
}
.fa-podcast
:before
{
content
:
"\f2ce"
;
}
.fa-window-maximize
:before
{
content
:
"\f2d0"
;
}
.fa-window-minimize
:before
{
content
:
"\f2d1"
;
}
.fa-window-restore
:before
{
content
:
"\f2d2"
;
}
.fa-times-rectangle
:before
,
.fa-window-close
:before
{
content
:
"\f2d3"
;
}
.fa-times-rectangle-o
:before
,
.fa-window-close-o
:before
{
content
:
"\f2d4"
;
}
.fa-bandcamp
:before
{
content
:
"\f2d5"
;
}
.fa-grav
:before
{
content
:
"\f2d6"
;
}
.fa-etsy
:before
{
content
:
"\f2d7"
;
}
.fa-imdb
:before
{
content
:
"\f2d8"
;
}
.fa-ravelry
:before
{
content
:
"\f2d9"
;
}
.fa-eercast
:before
{
content
:
"\f2da"
;
}
.fa-microchip
:before
{
content
:
"\f2db"
;
}
.fa-snowflake-o
:before
{
content
:
"\f2dc"
;
}
.fa-superpowers
:before
{
content
:
"\f2dd"
;
}
.fa-wpexplorer
:before
{
content
:
"\f2de"
;
}
.fa-meetup
:before
{
content
:
"\f2e0"
;
}
.sr-only
{
position
:
absolute
;
width
:
1px
;
height
:
1px
;
padding
:
0
;
margin
:
-1px
;
overflow
:
hidden
;
clip
:
rect
(
0
,
0
,
0
,
0
);
border
:
0
;
}
.sr-only-focusable
:active
,
.sr-only-focusable
:focus
{
position
:
static
;
width
:
auto
;
height
:
auto
;
margin
:
0
;
overflow
:
visible
;
clip
:
auto
;
}
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_nojquery_css.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Style"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_nojquery.css
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_nojquery_css
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<string>
en
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Style
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
RenderJS Gadget ERP5 CSS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
001
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
<title>
OfficeJS Chat
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"jiodev.js"
></script>
<script
src=
"gadget_global.js"
></script>
<script
src=
"fast_priority_queue.js"
></script>
<script
src=
"gadget_erp5_page_chat_box.js"
></script>
</head>
<body>
<div
class=
"chat-box"
>
<div
class=
"chat-left-panel"
>
<h3
class=
"center"
>
Contacts
</h3>
<ul
class=
"contact-list"
>
</ul>
<div
class=
"chat-form-panel"
>
<form
class=
"sync-form"
>
<input
type=
"submit"
value=
"Synchronize!"
/>
</form>
<form
class=
"edit-form"
>
<input
type=
"submit"
value=
"Edit contact!"
/>
</form>
<form
class=
"join-form"
>
<input
type=
"text"
name=
"content"
/>
<input
type=
"submit"
value=
"Add new contact (join existing room as guest)!"
/>
</form>
<form
class=
"make-form"
>
<input
type=
"text"
name=
"content"
/>
<input
type=
"submit"
value=
"Add new room (make new room as host)!"
/>
</form>
</div>
</div>
<div
class=
"chat-right-panel"
>
<div
class=
"chat-max-height-wrapper"
>
<div
class=
"chat-right-panel-chat"
>
<h3
class=
"chat-title center"
></h3>
<ul
class=
"chat-list"
>
</ul>
<form
class=
"send-form"
>
<input
type=
"text"
name=
"content"
/>
<input
type=
"submit"
name=
"send"
value=
"Send!"
/>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
<string>
contributor/person_module/1
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_box.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_box_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Page Chat Box
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_js.js
0 → 100644
View file @
edcd20f8
/*jslint nomen: true, indent: 2, maxerr: 300, maxlen: 80*/
/*global window, document, RSVP, rJS, FastPriorityQueue*/
(
function
(
window
,
document
,
RSVP
,
rJS
,
FastPriorityQueue
)
{
"
use strict
"
;
/* Settings required:
* - jio_storage_description, or be redirected
* - user_email
*/
/* Abstract Data Types:
* message is a JSON object representing a message with all its metadata.
* chat is messageToChat(message) for displaying an element in the chat log.
* Message Types:
* message is a typical message, with an author and timestamp.
* notification is a system-generated message, with no author.
* bundle is a list of new messages to be added to the local archive.
* request is a dictionary of the times of the last messages of each peer.
*/
/* Program Workflow:
* JIO storage (the "archive") can only bepag accessed through two functions:
* - getLocalArchive takes the archive and stores it in the list
* - storeArchive stores a message in the archive
* Local storage (the "list") is always in sorted order and
* can only be accessed through three functions:
* - sendLocalArchive takes the list and sends it over WebRTC
* - storeList stores a message in the list
* - refreshChat sorts the entire list
* The displayed chat log can only be accessed through two functions:
* - appendMessage turns a message into a chat and appends it to the log
* - refreshChat takes the list to overwrite chat log
* Peer interaction via WebRTC can only be accessed through four functions:
* - deployMessage sends a message over WebRTC
* - getMessage gets a message over WebRTC
* - sendLocalArchive sends the list over WebRTC
* - getRemoteArchive gets a list over WebRTC
*/
/* Summary:
* - getLocalArchive takes archive, calls storeList, appendMessage on each
* - sendLocalArchive sends the list to peer
* - getRemoteArchive gets a list from peer, calls storeArchive on each
* - deployMessage sends a message to peer, calls getMessage
* - getMessage gets peer, calls storeArchive, storeList, appendMessage
* - storeArchive stores a message in the archive
* - storeList stores a message in the list
* - appendMessage calls messageToChat, appends it to the chat log
* - refreshChat takes the list, overwrites the chat log
*/
function
logError
(
error
)
{
console
.
log
(
error
);
}
function
logQueue
(
action
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
action
;
})
.
push
(
null
,
logError
);
}
function
styleElementByQuery
(
gadget
,
query
,
style
)
{
gadget
.
element
.
querySelector
(
query
).
style
.
display
=
style
;
}
function
resetInputValue
(
element
)
{
const
value
=
element
.
value
;
element
.
value
=
""
;
return
value
;
}
function
getQueryValue
(
query_list
,
query_string
)
{
for
(
let
i
=
0
,
i_len
=
query_list
.
length
;
i
<
i_len
;
i
++
)
{
const
query
=
query_list
[
i
];
if
(
query_string
.
indexOf
(
query
+
"
=
"
)
!==
-
1
)
{
const
start
=
query_string
.
indexOf
(
query
+
"
=
"
)
+
query
.
length
+
1
;
let
end
=
query_string
.
indexOf
(
"
&
"
,
start
);
if
(
end
===
-
1
)
{
end
=
query_string
.
length
;
}
return
query_string
.
slice
(
start
,
end
);
}
}
return
null
;
}
function
setQueryValue
(
query_list
,
query_string
,
element
)
{
const
value
=
getQueryValue
(
query_list
,
query_string
);
if
(
value
)
{
element
.
value
=
value
;
}
}
function
pollUntilNotNull
(
gadget
,
delay_ms
,
timeout_ms
,
nullableFunction
,
callbackFunction
)
{
if
(
callbackFunction
===
undefined
)
{
callbackFunction
=
function
()
{};
}
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
nullableFunction
();
})
.
push
(
function
(
result
)
{
if
(
result
!==
null
)
{
return
callbackFunction
(
result
);
}
else
{
return
RSVP
.
any
([
RSVP
.
timeout
(
timeout_ms
),
promiseDoWhile
(
function
()
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
RSVP
.
delay
(
delay_ms
);
})
.
push
(
function
()
{
return
nullableFunction
();
})
.
push
(
function
(
result
)
{
if
(
result
===
null
)
{
return
null
;
}
else
{
return
callbackFunction
(
result
);
}
})
.
push
(
function
(
nullable
)
{
return
nullable
===
null
;
})
.
push
(
null
,
logError
);
})
]);
}
});
}
/* Translate an arbitrary name to a querySelector-safe ID.
* Parameters:
* - name: the name to use as an ID
* Returns: name with all non-alphanumeric characters replaced by underscores
* Effects: none
*/
function
nameToRoom
(
name
)
{
return
name
.
replace
(
/
\W
/gi
,
"
_
"
);
}
/* Check if a string ends with another string.
* Parameters:
* - string: the string that may or may not end with the suffix string
* - suffix: the substring that may or may not be in the end of the string
* Returns: true if string ends with suffix, otherwise false
* Effects: none
*/
function
stringEndsWith
(
string
,
suffix
)
{
return
string
.
indexOf
(
suffix
,
string
.
length
-
suffix
.
length
)
!==
-
1
;
}
/* Get the creation epoch time of a message.
* Parameters:
* - message: the message whose creation time is desired
* Returns: the creation time of the message, in milliseconds since 1970.
* Effects: none
*/
function
getTime
(
message
)
{
return
new
Date
(
message
.
time
).
getTime
();
}
/* Check if a message is new.
* Parameters:
* - message: the message that may or may not be new
* - last_time: the time in milliseconds since 1970 that splits old from new
* Returns: true if the message was created after last_time, otherwise false
* Effects: none
*/
function
isNewMessage
(
message
,
last_time
)
{
return
last_time
===
undefined
||
last_time
<
getTime
(
message
);
}
/* Check if two messages are the same.
* Parameters:
* - lhs, rhs: the two messages that may or may not be the same
* Returns: true if both messages exist and have equivalent properties.
* Effects: none
*/
function
isSameMessage
(
lhs
,
rhs
)
{
return
lhs
!==
undefined
&&
rhs
!==
undefined
&&
lhs
.
name
===
rhs
.
name
&&
lhs
.
content
===
rhs
.
content
&&
lhs
.
room
===
rhs
.
room
&&
getTime
(
lhs
)
===
getTime
(
rhs
);
}
/* Keep messages in chronological order, for FastPriorityQueue.
* Parameters:
* - ascending_order: true if the order is ascending, otherwise false
* Returns: a comparator for sorting messages using FastPriorityQueue
* Effects: none
*/
function
messageTimeCompare
(
ascending_order
)
{
if
(
ascending_order
)
{
return
function
(
lhs
,
rhs
)
{
return
getTime
(
lhs
)
<
getTime
(
rhs
);
};
}
return
function
(
lhs
,
rhs
)
{
return
getTime
(
lhs
)
>
getTime
(
rhs
);
};
}
/* Keep messages in chronologically ascending order, for refreshChat.
* Parameters:
* - lhs, rhs: two messages to compare during the sorting algorithm
* Returns: the correct value for sorting messages using sort()
* Effects: none
*/
function
messageTimeComparator
(
lhs
,
rhs
)
{
if
(
getTime
(
lhs
)
<
getTime
(
rhs
))
{
return
-
1
;
}
if
(
getTime
(
lhs
)
>
getTime
(
rhs
))
{
return
1
;
}
return
0
;
}
/* Translate a JSON message to an HTML chat element.
* Parameters:
* - message: the JavaScript object to display in HTML
* Returns: a HTML representation of the given message
* Effects: none
*/
function
messageToChat
(
message
)
{
var
matches
,
i
,
i_len
,
j
,
j_len
,
image
,
link
,
link_string
,
dot_count
,
is_image
,
absolute_url
,
chat
=
document
.
createElement
(
"
li
"
),
image_extensions
=
[
"
.jpg
"
,
"
.png
"
,
"
.gif
"
,
"
.bmp
"
,
"
.tif
"
,
"
.svg
"
],
url_regex
=
/
((?:
https
?
:
\/\/)?(?:[\-
a-zA-Z0-9_~
\/]
+
\.)
+
[\-
a-zA-Z0-9_~#?&=
\/]
+
)
/g
;
switch
(
message
.
type
)
{
case
"
bundle
"
:
break
;
case
"
notification
"
:
chat
.
appendChild
(
document
.
createTextNode
(
message
.
content
));
chat
.
style
.
color
=
message
.
colour
;
return
chat
;
case
"
message
"
:
// Put message in the format "[3/24/2017, 11:30:52 AM] user: message"
chat
.
appendChild
(
document
.
createTextNode
(
"
[
"
+
new
Date
(
message
.
time
).
toLocaleString
()
+
"
]
"
+
message
.
name
+
"
:
"
));
// Loop through all potential URLs in the message
// matches[i] is the non-link content, matches[i + 1] is the actual link
matches
=
message
.
content
.
split
(
url_regex
);
for
(
i
=
0
,
i_len
=
matches
.
length
-
1
;
i
<
i_len
;
i
+=
2
)
{
chat
.
appendChild
(
document
.
createTextNode
(
matches
[
i
]));
link_string
=
matches
[
i
+
1
];
dot_count
=
link_string
.
match
(
/
\.
/g
).
length
;
// If 2d + 1 >= L, then the potential URL has only single letters,
// so ignore it to eliminate acronyms like e.g., i.e., U.S.A., etc.
if
(
2
*
dot_count
+
1
>=
link_string
.
length
)
{
chat
.
appendChild
(
document
.
createTextNode
(
link_string
));
}
else
{
is_image
=
false
;
// Add a protocol to transform relative URLs into absolute URLs
if
(
link_string
.
indexOf
(
"
:
"
)
!==
-
1
)
{
absolute_url
=
link_string
;
}
else
{
absolute_url
=
"
http://
"
+
link_string
;
}
// Create an image if the URL ends with one of the image extensions
for
(
j
=
0
,
j_len
=
image_extensions
.
length
;
j
<
j_len
;
j
+=
1
)
{
if
(
stringEndsWith
(
link_string
,
image_extensions
[
j
]))
{
image
=
document
.
createElement
(
"
img
"
);
image
.
src
=
absolute_url
;
chat
.
appendChild
(
image
);
is_image
=
true
;
break
;
}
}
// Otherwise, create a link
if
(
!
is_image
)
{
link
=
document
.
createElement
(
"
a
"
);
link
.
href
=
absolute_url
;
link
.
innerHTML
=
link_string
;
chat
.
appendChild
(
link
);
}
}
}
// Add the last non-link content and return the resulting chat element
chat
.
appendChild
(
document
.
createTextNode
(
matches
[
matches
.
length
-
1
]));
chat
.
style
.
color
=
message
.
colour
;
return
chat
;
}
}
rJS
(
window
)
.
setState
({
name
:
null
,
auth
:
null
,
dropbox_folder
:
null
,
erp5_url
:
null
,
room
:
null
,
element
:
null
,
initialized
:
false
,
// A set of names of current rooms; as a guest, only notify that
// you have joined once you have received the host's remote archive,
// so that your notification doesn't override all previous messages
// room_set["room"] = false if joined, true if also notified
room_set
:
{},
// A dictionary of messages based on the current room, e.g.
// message_list_dict["room"] = [message1, message2, ...]
message_list_dict
:
{},
// A dictionary of epoch times of the last messages received, e.g.
// last_message_dict["room"]["name"] = 1234567890
last_message_dict
:
{},
alert_icon
:
"
https://pricespy.co.nz/favicon.ico
"
,
default_icon
:
"
https://softinst75770.host.vifib.net/
"
+
"
erp5/web_site_module/web_chat/favicon.ico
"
})
.
declareService
(
function
()
{
var
gadget
=
this
;
return
gadget
.
render
();
})
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
"
updateHeader
"
,
"
updateHeader
"
)
.
declareAcquiredMethod
(
"
getSetting
"
,
"
getSetting
"
)
.
declareAcquiredMethod
(
"
requireSetting
"
,
"
requireSetting
"
)
.
declareAcquiredMethod
(
"
jio_allDocs
"
,
"
jio_allDocs
"
)
.
declareAcquiredMethod
(
"
jio_put
"
,
"
jio_put
"
)
.
declareAcquiredMethod
(
"
jio_repair
"
,
"
jio_repair
"
)
/* Get the contact's jIO ID from their email address.
* Parameters:
* - param_list[0]: the email address of the contact
* Returns: the ID of the contact if found in jIO storage, otherwise null
* Effects: none
*/
.
allowPublicAcquisition
(
"
getContactByEmail
"
,
function
(
param_list
)
{
var
gadget
=
this
,
email
=
param_list
[
0
],
i
,
i_len
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
jio_allDocs
({
limit
:
[
0
,
1000000
],
query
:
'
portal_type: "Person"
'
,
sort_on
:
[[
"
last_modified
"
,
"
descending
"
]],
select_list
:
[
"
default_email_coordinate_text
"
],
});
})
.
push
(
function
(
person_list
)
{
for
(
i
=
0
,
i_len
=
person_list
.
data
.
total_rows
;
i
<
i_len
;
i
+=
1
)
{
if
(
person_list
.
data
.
rows
[
i
].
value
.
default_email_coordinate_text
===
email
)
{
return
person_list
.
data
.
rows
[
i
].
id
;
}
}
return
null
;
})
.
push
(
null
,
logError
);
})
/* Render the gadget
* Parameters:
* - get setting: jio_storage_description, user_email
* Effects:
* - update header, page_title to "OfficeJS Chat"
* - redirect if no jIO storage available
* - join the user home room whose name is the user name
*/
.
declareMethod
(
"
render
"
,
function
()
{
var
gadget
=
this
;
return
gadget
.
requireSetting
(
"
jio_storage_description
"
,
"
jio_configurator
"
,
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
updateHeader
({
page_title
:
"
OfficeJS Chat
"
,
});
})
.
push
(
function
()
{
return
gadget
.
getSetting
(
"
user_email
"
);
})
.
push
(
function
(
email
)
{
gadget
.
state
.
room
=
email
.
replace
(
/
[^
0-9a-z
\-]
/gi
,
"
-
"
);
gadget
.
state
.
name
=
gadget
.
state
.
room
;
gadget
.
element
.
querySelector
(
"
.send-form input[type='text']
"
).
onfocus
=
function
()
{
return
logQueue
(
gadget
.
notifyStatus
(
gadget
.
state
.
room
,
false
));
};
})
.
push
(
function
()
{
return
gadget
.
createContact
({
room
:
gadget
.
state
.
room
,
role
:
"
host
"
});
})
);
})
/* Send a message via the appropriate WebRTC channel.
* Parameters:
* - message: the message to send, whose room is the selected WebRTC room
* - source: the WebRTC data channel that sent the original message
* Returns: nothing
* Effects: calls sendMessage on the WebRTC gadget
*/
.
declareMethod
(
"
sendMessage
"
,
function
(
message
,
source
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getDeclaredGadget
(
"
webrtc_gadget_
"
+
message
.
room
);
})
.
push
(
function
(
webrtc_gadget
)
{
return
webrtc_gadget
.
sendMessage
.
apply
(
webrtc_gadget
,
[
message
,
source
]);
})
.
push
(
null
,
logError
);
})
.
declareMethod
(
"
chooseRoom
"
,
function
(
param_dict
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
declareGadget
(
"
gadget_erp5_chat_webrtc.html
"
,
{
scope
:
"
webrtc_gadget_
"
+
param_dict
.
room
});
})
.
push
(
function
(
webrtc_gadget
)
{
var
fields
=
webrtc_gadget
.
element
.
querySelector
(
"
.contact-form
"
).
elements
;
fields
.
dropbox_folder
.
value
=
param_dict
.
dropbox_folder
||
gadget
.
state
.
dropbox_folder
;
fields
.
erp5_url
.
value
=
param_dict
.
erp5_url
||
gadget
.
state
.
erp5_url
;
webrtc_gadget
.
element
.
querySelector
(
"
.auth-form
"
)
.
auth
.
value
=
param_dict
.
auth
||
gadget
.
state
.
auth
;
gadget
.
element
.
querySelector
(
"
.chat-right-panel
"
).
insertBefore
(
webrtc_gadget
.
element
,
gadget
.
element
.
querySelector
(
"
.chat-max-height-wrapper
"
));
webrtc_gadget
.
state
.
login_dict
=
{
room
:
param_dict
.
room
,
name
:
gadget
.
state
.
name
,
role
:
param_dict
.
role
,
};
webrtc_gadget
.
state
.
dataChannelOnopen
=
function
()
{
return
gadget
.
changeRoom
(
param_dict
.
room
);
};
webrtc_gadget
.
state
.
dataChannelOnmessage
=
function
(
event
)
{
var
message
=
JSON
.
parse
(
event
.
data
);
var
source
=
event
.
srcElement
;
if
(
message
.
type
===
"
notification
"
||
message
.
type
===
"
message
"
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getMessage
(
message
);
})
.
push
(
function
()
{
if
(
param_dict
.
role
===
"
host
"
)
{
return
webrtc_gadget
.
sendMessage
(
message
,
source
);
}
else
{
return
;
}
})
.
push
(
null
,
logError
);
}
else
if
(
message
.
type
===
"
bundle
"
)
{
if
(
param_dict
.
role
===
"
host
"
)
{
webrtc_gadget
.
state
.
archive_amount
+=
1
;
if
(
webrtc_gadget
.
state
.
archive_amount
>=
webrtc_gadget
.
state
.
guest_amount
)
{
webrtc_gadget
.
state
.
archive_amount
=
0
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getRemoteArchive
(
message
);
})
.
push
(
function
()
{
return
gadget
.
requestRequest
();
})
.
push
(
null
,
logError
);
}
}
else
{
return
gadget
.
getRemoteArchive
(
message
);
}
}
else
if
(
message
.
type
===
"
request
"
)
{
return
gadget
.
sendLocalArchive
(
message
,
source
);
}
else
if
(
message
.
type
===
"
doubler
"
)
{
return
gadget
.
sendRequest
();
}
};
return
webrtc_gadget
.
render
();
})
.
push
(
null
,
logError
);
})
/*
return gadget.createJio({
remote: fields.remote.value,
dropbox_folder: fields.remote_dropbox_folder.value,
erp5_url: fields.remote_erp5_url.value,
dav_url: fields.remote_dav_url.value,
dav_user: fields.remote_dav_user.value,
dav_pass: fields.remote_dav_pass.value,
});
})
.push(function () {
return gadget.jio_repair();
})
*/
/*
.declareMethod("createJio", function (param_dict) {
var gadget = this;
return new RSVP.Queue()
.push(function () {
var remote_config = {
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: "officejs-chat",
},
},
};
var remote_post = true;
// We want the remote ERP5 storage to be canonical, so true for ERP5.
// Post is not implemented in drivetojiomapping, so false for others.
switch (param_dict.remote) {
case "dropbox":
var start = document.URL.indexOf("access_token=") + 13;
var end = document.URL.indexOf("&", start);
var token = document.URL.slice(start, end);
remote_config = {
type: "query",
sub_storage: {
type: "drivetojiomapping",
sub_storage: {
type: "dropbox",
access_token: token,
root: "dropbox",
},
},
};
remote_post = false;
break;
case "dav":
remote_config = {
type: "query",
sub_storage: {
type: "drivetojiomapping",
sub_storage: {
type: "dav",
url: param_dict.dav_url,
basic_login: btoa(param_dict.dav_user
+ ":" + param_dict.dav_pass),
with_credentials: true,
},
},
};
remote_post = false;
break;
case "erp5":
remote_config = {
type: "erp5",
url: (new URI("hateoas"))
.absoluteTo(param_dict.erp5_url).toString(),
default_view_reference: "view",
};
remote_post = true;
break;
}
return gadget.createJio({
type: "replicate",
use_remote_post: remote_post,
conflict_handling: 2,
query: {
limit: [0, 1000000000],
query: "portal_type: "Person" OR portal_type: "Text Post"",
},
local_sub_storage: {
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: "officejs-chat",
},
},
},
remote_sub_storage: remote_config,
});
})
.push(null, logError);
})
*/
// Get all messages stored in archive and add them to the list in order,
// using storeList because the messages are already in the archive
.
declareMethod
(
"
getLocalArchive
"
,
function
()
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
jio_allDocs
({
limit
:
[
0
,
1000000
],
query
:
'
portal_type: "Text Post"
'
,
sort_on
:
[[
"
last_modified
"
,
"
descending
"
]],
select_list
:
[
"
content
"
],
});
})
.
push
(
function
(
message_list
)
{
var
message_queue
=
new
FastPriorityQueue
(
messageTimeCompare
(
true
)),
i
,
i_len
;
for
(
i
=
0
,
i_len
=
message_list
.
data
.
total_rows
;
i
<
i_len
;
i
+=
1
)
{
try
{
var
message
=
JSON
.
parse
(
list
[
i
]);
if
(
message
&&
typeof
message
===
"
object
"
)
{
message_queue
.
add
(
message
);
}
}
catch
(
error
)
{}
}
var
promise_list
=
[];
var
message_dict
=
gadget
.
state
.
last_message_dict
;
while
(
!
message_queue
.
isEmpty
())
{
var
message
=
message_queue
.
poll
();
if
(
message
.
room
in
gadget
.
state
.
room_set
)
{
promise_list
.
push
(
gadget
.
storeList
(
message
));
promise_list
.
push
(
gadget
.
appendMessage
(
message
));
}
}
gadget
.
state
.
initialized
=
true
;
return
RSVP
.
all
(
promise_list
);
})
.
push
(
null
,
logError
);
})
// Send all requested messages in the list, in sorted order, to peer
.
declareMethod
(
"
sendLocalArchive
"
,
function
(
request
,
source
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
var
request_dict
=
{};
var
room
;
for
(
room
in
request
.
content
.
room_set
)
{
if
(
request
.
content
.
room_set
.
hasOwnProperty
(
room
)
&&
gadget
.
state
.
message_list_dict
[
room
])
{
request_dict
[
room
]
=
[];
var
list
=
gadget
.
state
.
message_list_dict
[
room
],
i
,
i_len
;
for
(
i
=
0
,
i_len
=
list
.
length
;
i
<
i_len
;
i
+=
1
)
{
if
(
isNewMessage
(
list
[
i
],
request
.
content
.
dict
[
room
][
list
[
i
].
name
]))
{
request_dict
[
room
].
push
(
list
[
i
]);
}
}
}
}
return
gadget
.
createMessage
({
type
:
"
bundle
"
,
content
:
request_dict
});
})
.
push
(
function
(
message
)
{
return
gadget
.
sendMessage
(
message
,
source
);
})
.
push
(
null
,
logError
);
})
// Get all new messages from the sorted list of peer,
// add them to both the archive and list using storeArchive,
// and refresh, dedupe, and resort the list using refreshChat
.
declareMethod
(
"
getRemoteArchive
"
,
function
(
bundle
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
var
promise_list
=
[],
room
;
for
(
room
in
bundle
.
content
)
{
if
(
bundle
.
content
.
hasOwnProperty
(
room
))
{
var
list
=
gadget
.
state
.
message_list_dict
[
room
];
var
remote_list
=
bundle
.
content
[
room
],
i
,
i_len
;
for
(
i
=
0
,
i_len
=
remote_list
.
length
;
i
<
i_len
;
i
+=
1
)
{
promise_list
.
push
(
gadget
.
storeList
(
remote_list
[
i
]));
promise_list
.
push
(
gadget
.
storeArchive
(
remote_list
[
i
]));
}
}
}
return
RSVP
.
all
(
promise_list
);
})
.
push
(
function
()
{
if
(
!
gadget
.
state
.
room_set
[
gadget
.
state
.
room
])
{
gadget
.
state
.
room_set
[
gadget
.
state
.
room
]
=
true
;
return
gadget
.
deployMessage
({
type
:
"
notification
"
,
content
:
gadget
.
state
.
name
+
"
has joined.
"
,
colour
:
"
orange
"
,
});
}
else
{
return
;
}
})
.
push
(
function
()
{
return
gadget
.
refreshChat
();
})
.
push
(
null
,
logError
);
})
// Ask a peer to send over a request
.
declareMethod
(
"
requestRequest
"
,
function
()
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
createMessage
({
type
:
"
doubler
"
});
})
.
push
(
function
(
message
)
{
return
gadget
.
sendMessage
(
message
);
})
.
push
(
null
,
logError
);
})
// Send a request to update the local archive
.
declareMethod
(
"
sendRequest
"
,
function
()
{
var
gadget
=
this
;
return
pollUntilNotNull
(
gadget
,
1000
,
30000
,
function
()
{
return
gadget
.
state
.
initialized
;
},
function
()
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
createMessage
({
type
:
"
request
"
,
content
:
{
room_set
:
gadget
.
state
.
room_set
,
dict
:
gadget
.
state
.
last_message_dict
,
},
});
})
.
push
(
function
(
message
)
{
return
gadget
.
sendMessage
(
message
);
})
.
push
(
null
,
logError
);
});
})
// Create new message from its parameters
.
declareMethod
(
"
createMessage
"
,
function
(
param_dict
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
{
type
:
param_dict
.
type
||
"
message
"
,
name
:
param_dict
.
name
||
gadget
.
state
.
name
,
room
:
param_dict
.
room
||
gadget
.
state
.
room
,
time
:
param_dict
.
time
||
new
Date
(),
content
:
param_dict
.
content
||
""
,
colour
:
param_dict
.
colour
||
"
black
"
,
};
})
.
push
(
null
,
logError
);
})
// Create new message and send it to peer
.
declareMethod
(
"
deployMessage
"
,
function
(
param_dict
)
{
var
gadget
=
this
;
var
message
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
createMessage
(
param_dict
);
})
.
push
(
function
(
result
)
{
message
=
result
;
return
gadget
.
storeList
(
message
);
})
.
push
(
function
()
{
return
gadget
.
appendMessage
(
message
);
})
.
push
(
function
()
{
return
gadget
.
storeArchive
(
message
);
})
.
push
(
function
()
{
return
gadget
.
sendMessage
(
message
);
})
.
push
(
null
,
logError
);
})
// Create new notification and keep it on own machine
.
declareMethod
(
"
deployNotification
"
,
function
(
param_dict
)
{
var
gadget
=
this
;
var
notification
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
param_dict
.
type
=
"
notification
"
;
return
gadget
.
createMessage
(
param_dict
);
})
.
push
(
function
(
result
)
{
notification
=
result
;
return
gadget
.
storeList
(
notification
);
})
.
push
(
function
()
{
return
gadget
.
appendMessage
(
notification
);
})
.
push
(
null
,
logError
);
})
// Get message from peer, store it in archive and list
.
declareMethod
(
"
getMessage
"
,
function
(
message
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
notifyStatus
(
message
.
room
,
true
);
})
.
push
(
function
()
{
return
gadget
.
storeList
(
message
);
})
.
push
(
function
()
{
return
gadget
.
appendMessage
(
message
);
})
.
push
(
function
()
{
return
gadget
.
storeArchive
(
message
);
})
.
push
(
null
,
logError
);
})
// Store message in the archive
.
declareMethod
(
"
storeArchive
"
,
function
(
message
)
{
var
gadget
=
this
;
var
id
=
message
.
room
+
"
_
"
+
message
.
name
+
"
_
"
+
getTime
(
message
).
toString
();
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
jio_put
(
id
,
{
portal_type
:
"
Text Post
"
,
parent_relative_url
:
"
post_text_module
"
,
reference
:
id
,
author
:
message
.
name
,
date_ms
:
getTime
(
message
),
content
:
JSON
.
stringify
(
message
),
});
})
.
push
(
null
,
logError
);
})
// Add message to the list
.
declareMethod
(
"
storeList
"
,
function
(
message
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
isNewMessage
(
message
,
gadget
.
state
.
last_message_dict
[
message
.
room
][
message
.
name
]))
{
gadget
.
state
.
last_message_dict
[
message
.
room
][
message
.
name
]
=
getTime
(
message
);
}
gadget
.
state
.
message_list_dict
[
message
.
room
].
push
(
message
);
})
.
push
(
null
,
logError
);
})
// Appends a message to the chat box
.
declareMethod
(
"
appendMessage
"
,
function
(
message
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
message
.
room
===
gadget
.
state
.
room
)
{
var
container
=
gadget
.
element
.
querySelector
(
"
.chat-list
"
);
container
.
appendChild
(
messageToChat
(
message
));
container
.
scrollTop
=
container
.
scrollHeight
;
/*
gadget.element
.querySelector(".chat-right-panel").style.height =
gadget.element
.querySelector(".chat-left-panel").style.height;
*/
}
})
.
push
(
null
,
logError
);
})
// Sort the list, dedupe, and overwrite the chat box,
// efficient because the archive is originally sorted
.
declareMethod
(
"
refreshChat
"
,
function
()
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
var
container
=
gadget
.
element
.
querySelector
(
"
.chat-list
"
);
while
(
container
.
firstChild
)
{
container
.
removeChild
(
container
.
firstChild
);
}
var
old_list
=
gadget
.
state
.
message_list_dict
[
gadget
.
state
.
room
];
old_list
.
sort
(
messageTimeComparator
);
var
new_list
=
[];
var
last_message
,
i
,
i_len
;
for
(
i
=
0
,
i_len
=
old_list
.
length
;
i
<
i_len
;
i
+=
1
)
{
var
message
=
old_list
[
i
];
if
(
isSameMessage
(
last_message
,
message
))
{
continue
;
}
last_message
=
message
;
new_list
.
push
(
message
);
container
.
appendChild
(
messageToChat
(
message
));
}
gadget
.
state
.
message_list_dict
[
gadget
.
state
.
room
]
=
new_list
;
container
.
scrollTop
=
container
.
scrollHeight
;
/*
gadget.element
.querySelector(".chat-right-panel").style.height =
gadget.element
.querySelector(".chat-left-panel").style.height;
*/
})
.
push
(
null
,
logError
);
})
// Notify when a new message appears and denotify when it is seen
.
declareMethod
(
"
notifyStatus
"
,
function
(
room
,
notify
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
var
favicon_url
;
var
class_name
;
if
(
notify
)
{
favicon_url
=
gadget
.
state
.
alert_icon
;
class_name
=
"
notify
"
;
}
else
{
favicon_url
=
gadget
.
state
.
default_icon
;
class_name
=
"
current
"
;
}
var
link
=
document
.
querySelector
(
"
link[rel*='icon']
"
)
||
document
.
createElement
(
"
link
"
);
link
.
type
=
"
image/x-icon
"
;
link
.
rel
=
"
shortcut icon
"
;
link
.
href
=
favicon_url
;
document
.
head
.
appendChild
(
link
);
var
contact
=
gadget
.
element
.
querySelector
(
"
#chat-contact-
"
+
room
);
contact
.
className
=
class_name
;
})
.
push
(
null
,
logError
);
})
// Join a different room
.
declareMethod
(
"
changeRoom
"
,
function
(
room
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
styleElementByQuery
(
gadget
,
"
#webrtc-gadget-
"
+
gadget
.
state
.
room
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
.chat-right-panel-chat
"
,
"
flex
"
);
gadget
.
element
.
querySelector
(
"
.chat-title
"
)
.
textContent
=
"
Room:
"
+
room
;
gadget
.
state
.
room
=
room
;
if
(
!
(
room
in
gadget
.
state
.
room_set
))
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
gadget
.
state
.
room_set
[
room
]
=
false
;
gadget
.
state
.
message_list_dict
[
room
]
=
[];
gadget
.
state
.
last_message_dict
[
room
]
=
{};
})
.
push
(
function
()
{
return
gadget
.
getLocalArchive
();
})
.
push
(
function
()
{
return
gadget
.
requestRequest
();
})
.
push
(
null
,
logError
);
}
})
.
push
(
function
()
{
return
gadget
.
refreshChat
();
})
.
push
(
null
,
logError
);
})
// Create a new contact
.
declareMethod
(
"
createContact
"
,
function
(
param_dict
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
appendContact
(
param_dict
.
room
);
})
.
push
(
function
()
{
return
gadget
.
chooseRoom
(
param_dict
);
})
.
push
(
function
()
{
return
gadget
.
editContact
(
param_dict
.
room
);
})
.
push
(
null
,
logError
);
})
// Add a contact as some HTML element
.
declareMethod
(
"
appendContact
"
,
function
(
room
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
!
room
.
trim
())
{
throw
"
An invisible name is not allowed! You couldn't click on it!
"
;
}
if
(
gadget
.
element
.
querySelector
(
"
#chat-contact-
"
+
room
))
{
throw
"
A contact with the same name already exists!
"
;
}
var
contact
=
document
.
createElement
(
"
li
"
);
contact
.
appendChild
(
document
.
createTextNode
(
room
));
contact
.
setAttribute
(
"
id
"
,
"
chat-contact-
"
+
room
);
gadget
.
element
.
querySelector
(
"
.contact-list
"
).
appendChild
(
contact
);
gadget
.
element
.
querySelector
(
"
#chat-contact-
"
+
gadget
.
state
.
room
).
className
=
""
;
contact
.
className
=
"
current
"
;
contact
.
addEventListener
(
"
click
"
,
function
(
event
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
notifyStatus
(
room
,
false
);
})
.
push
(
function
()
{
gadget
.
element
.
querySelector
(
"
#chat-contact-
"
+
gadget
.
state
.
room
).
className
=
""
;
gadget
.
element
.
querySelector
(
"
#chat-contact-
"
+
room
).
className
=
"
current
"
;
})
.
push
(
function
()
{
if
(
gadget
.
state
.
room_set
[
room
])
{
return
gadget
.
changeRoom
(
room
);
}
else
{
return
gadget
.
editContact
(
room
);
}
})
.
push
(
null
,
logError
);
},
false
);
return
;
})
})
// Edit a contact in the right panel
.
declareMethod
(
"
editContact
"
,
function
(
room
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
styleElementByQuery
(
gadget
,
"
.chat-right-panel-chat
"
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
#webrtc-gadget-
"
+
gadget
.
state
.
room
,
"
none
"
);
styleElementByQuery
(
gadget
,
"
#webrtc-gadget-
"
+
room
,
"
block
"
);
gadget
.
state
.
room
=
room
;
gadget
.
element
.
querySelector
(
"
.chat-title
"
)
.
textContent
=
"
Contact:
"
+
room
;
})
.
push
(
null
,
logError
);
})
// Parse chat commands
.
declareMethod
(
"
parseCommand
"
,
function
(
chat
)
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
var
split
=
chat
.
slice
(
1
).
split
(
"
"
);
var
command
=
split
.
shift
();
var
argument
=
split
.
join
(
"
"
);
switch
(
command
)
{
case
"
join
"
:
if
(
gadget
.
state
.
room_set
[
argument
])
{
return
gadget
.
changeRoom
(
argument
);
}
else
{
return
gadget
.
deployNotification
({
content
:
"
You must first be connected to room '
"
+
argument
+
"
' via WebRTC to join it!
"
,
colour
:
"
red
"
,
});
}
case
"
leave
"
:
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
if
(
gadget
.
state
.
room
===
gadget
.
state
.
name
)
{
return
gadget
.
deployNotification
({
content
:
"
You cannot leave your own room!
"
,
colour
:
"
red
"
});
}
else
{
delete
gadget
.
state
.
room_set
[
gadget
.
state
.
room
];
return
gadget
.
deployMessage
({
type
:
"
notification
"
,
content
:
gadget
.
state
.
name
+
"
has quit.
"
,
colour
:
"
orange
"
,
});
}
})
.
push
(
function
()
{
return
gadget
.
changeRoom
(
gadget
.
state
.
name
);
})
.
push
(
null
,
logError
);
case
"
quit
"
:
window
.
location
.
reload
();
break
;
case
"
help
"
:
var
help_list
=
[
"
Available commands:
"
,
"
/join [room]: connects you to [room]
"
,
"
/help: displays this help box
"
,
"
/leave: disconnects you from the current room
"
,
"
/quit: disconnects from the chat and refreshes the page
"
,
];
var
promise_list
=
[];
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
var
i
,
i_len
;
for
(
i
=
0
,
i_len
=
help_list
.
length
;
i
<
i_len
;
i
+=
1
)
{
promise_list
.
push
(
gadget
.
deployNotification
({
content
:
help_list
[
i
],
colour
:
"
green
"
,
}));
}
return
RSVP
.
all
(
promise_list
);
})
.
push
(
null
,
logError
);
}
});
})
.
onEvent
(
"
submit
"
,
function
(
event
)
{
var
gadget
=
this
;
switch
(
event
.
target
.
className
)
{
case
"
sync-form
"
:
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
jio_repair
();
})
.
push
(
function
()
{
return
gadget
.
getLocalArchive
();
})
.
push
(
function
()
{
return
gadget
.
requestRequest
();
})
.
push
(
null
,
logError
);
case
"
edit-form
"
:
return
gadget
.
editContact
(
gadget
.
state
.
room
);
case
"
join-form
"
:
var
contact
=
resetInputValue
(
event
.
target
.
elements
.
content
);
return
gadget
.
createContact
({
room
:
contact
,
role
:
"
guest
"
});
case
"
make-form
"
:
var
room
=
resetInputValue
(
event
.
target
.
elements
.
content
);
return
gadget
.
createContact
({
room
:
room
,
role
:
"
host
"
});
case
"
send-form
"
:
var
content
=
resetInputValue
(
event
.
target
.
elements
.
content
);
if
(
content
.
indexOf
(
"
/
"
)
===
0
)
{
return
gadget
.
parseCommand
(
content
);
}
else
{
return
gadget
.
deployMessage
({
content
:
content
});
}
}
})
.
declareService
(
function
()
{
var
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
promiseEventListener
(
window
,
"
beforeunload
"
,
true
);
})
.
push
(
function
()
{
if
(
gadget
.
state
.
initialized
)
{
var
promise_list
=
[],
room
;
for
(
room
in
gadget
.
state
.
room_set
)
{
if
(
gadget
.
state
.
room_set
.
hasOwnProperty
(
room
)
&&
gadget
.
state
.
room_set
[
room
])
{
promise_list
.
push
(
gadget
.
deployMessage
({
type
:
"
notification
"
,
content
:
gadget
.
state
.
name
+
"
has quit.
"
,
room
:
room
,
colour
:
"
orange
"
,
}));
}
}
return
RSVP
.
all
(
promise_list
);
}
else
{
return
;
}
})
.
push
(
null
,
logError
);
});
}(
window
,
document
,
RSVP
,
rJS
,
FastPriorityQueue
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_box_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
<string>
contributor/person_module/1
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_box.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_box_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Page Chat Box JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, user-scalable=no"
>
<title>
OfficeJS Chat Connect
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"gadget_erp5_page_chat_connect.js"
></script>
</head>
<body>
<form
class=
"connect-form"
>
<label>
E-mail Address:
</label>
<input
type=
"email"
name=
"user_email"
required=
"required"
/>
<label>
Default WebRTC Authentication Method:
</label>
<br
/>
<input
type=
"radio"
name=
"default_webrtc_storage"
value=
"erp5"
>
ERP5
</input>
<input
type=
"radio"
name=
"default_webrtc_storage"
value=
"dropbox"
>
Dropbox
</input>
<br
/>
<label>
Default Dropbox Folder:
</label>
<input
type=
"text"
name=
"default_dropbox_folder"
placeholder=
"/Apps/OfficeJS Chat/"
/>
<label>
Default ERP5 URL:
</label>
<input
type=
"text"
name=
"default_erp5_url"
,
placeholder=
"https://softinst75770.host.vifib.net/erp5/webrtc_rooms_module/"
/>
<input
type=
"submit"
/>
</form>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_connect.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_connect_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<string>
en
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Chat Connect
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
001
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_js.js
0 → 100644
View file @
edcd20f8
/* global window, RSVP, rJS */
/* jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80 */
(
function
(
window
,
RSVP
,
rJS
)
{
'
use strict
'
;
/* Settings modified:
* - user_email
* - default_webrtc_storage
* - default_dropbox_folder
* - default_erp5_url
*/
/* Store the given connection settings
* Parameters: all from the connect form,
* - user_email: an email, which acts as a username, example: eugene@abc.xyz
* - default_webrtc_storage: the type of storage to select by default
* for sharing WebRTC negotiations, example: dropbox
* - default_dropbox_folder: the Dropbox folder to select by default
* for sharing WebRTC negotiations, example: /Apps/OfficeJS Chat/
* - default_erp5_url: the ERP5 URL to select by default for sharing,
* example: https://softinst75770.host.vifib.net/erp5/web_site_module/
* Effects:
* - set setting: user_email, default_webrtc_storage,
* default_dropbox_folder, default_erp5_url
* - redirect to the front page
*/
function
setConnectConfiguration
(
gadget
,
event
)
{
const
fields
=
[
'
user_email
'
,
'
default_webrtc_storage
'
,
'
default_dropbox_folder
'
,
'
default_erp5_url
'
];
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
const
queue
=
new
RSVP
.
Queue
();
for
(
let
i
=
0
,
i_len
=
fields
.
length
;
i
<
i_len
;
i
++
)
{
const
field
=
fields
[
i
];
if
(
event
.
target
.
hasOwnProperty
(
field
)
&&
event
.
target
[
field
].
value
)
{
queue
.
push
(
function
()
{
return
gadget
.
setSetting
(
field
,
event
.
target
[
field
].
value
);
});
}
}
return
queue
;
})
.
push
(
function
()
{
return
gadget
.
redirect
();
});
}
rJS
(
window
)
// Neither state to set nor ready to initialize
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
'
updateHeader
'
,
'
updateHeader
'
)
.
declareAcquiredMethod
(
'
redirect
'
,
'
redirect
'
)
.
declareAcquiredMethod
(
'
setSetting
'
,
'
setSetting
'
)
/* Render the gadget
* Parameters: nothing
* Effects:
* - update header, page_title to 'Connect to Chat'
*/
.
declareMethod
(
'
render
'
,
function
()
{
var
gadget
=
this
;
return
gadget
.
updateHeader
({
page_title
:
'
Connect to Chat
'
,
submit_action
:
true
});
})
/* Manually click submit button when the right button is clicked,
* so that HTML5 form validation is automatically used
*/
.
declareMethod
(
'
triggerSubmit
'
,
function
(
event
)
{
this
.
element
.
querySelector
(
'
input[type="submit"]
'
).
click
();
})
// Call setConnectionConfiguration when either proceed button is clicked
.
onEvent
(
'
submit
'
,
function
(
event
)
{
const
gadget
=
this
;
return
setConnectConfiguration
(
gadget
,
event
);
});
}(
window
,
RSVP
,
rJS
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_chat_connect_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_connect.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_chat_connect_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<string>
en
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Chat Connect JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
001
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, user-scalable=no"
>
<title>
jIO Configurator Gadget
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"gadget_erp5_page_contact_add.js"
></script>
</head>
<body>
</body>
</html>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
contributor/person_module/1
</string>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
creators
</string>
</key>
<value>
<tuple>
<string>
cedric.le.ninivin
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_add.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_add_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
modification_date
</string>
</key>
<value>
<object>
<klass>
<global
name=
"DateTime"
module=
"DateTime.DateTime"
/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>
1455284351.46
</float>
<string>
UTC
</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Contact Add
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_js.js
0 → 100644
View file @
edcd20f8
/*globals window, RSVP, rJS*/
/*jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80*/
(
function
(
window
,
RSVP
,
rJS
)
{
'
use strict
'
;
rJS
(
window
)
// Neither state to set nor ready to initialize
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
'
updateHeader
'
,
'
updateHeader
'
)
.
declareAcquiredMethod
(
'
redirect
'
,
'
redirect
'
)
.
declareAcquiredMethod
(
'
getSetting
'
,
'
getSetting
'
)
.
declareAcquiredMethod
(
'
jio_post
'
,
'
jio_post
'
)
/* Render the gadget
* Parameters: nothing
* XXX: portal_Type, document_title, parent_relative_url
* Returns: nothing
* Effects:
* - update header, page title to 'Add Contact'
* - post a new contact to jIO storage
*/
.
declareMethod
(
'
render
'
,
function
()
{
const
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
updateHeader
({
page_title
:
'
New Contact
'
});
})
.
push
(
function
()
{
const
doc
=
{
parent_relative_url
:
'
person_module
'
,
portal_type
:
'
Person
'
,
};
return
gadget
.
jio_post
(
doc
);
})
.
push
(
function
(
id
)
{
console
.
log
(
id
);
return
gadget
.
redirect
({
command
:
'
display
'
,
options
:
{
jio_key
:
id
,
page
:
'
jio_person_view
'
,
},
});
});
});
}(
window
,
RSVP
,
rJS
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_add_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
contributor/person_module/1
</string>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
creators
</string>
</key>
<value>
<tuple>
<string>
cedric.le.ninivin
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_add.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_add_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
modification_date
</string>
</key>
<value>
<object>
<klass>
<global
name=
"DateTime"
module=
"DateTime.DateTime"
/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>
1455284351.49
</float>
<string>
UTC
</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Contact Add JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, user-scalable=no"
>
<title>
OfficeJS Chat Contact List
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"gadget_erp5_page_contact_list.js"
></script>
</head>
<body>
<div
data-gadget-url=
"gadget_erp5_field_listbox_widget.html"
data-gadget-scope=
"listbox"
data-gadget-sandbox=
"public"
></div>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
contributor/person_module/1
</string>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
creators
</string>
</key>
<value>
<tuple>
<string>
cedric.le.ninivin
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_list.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_list_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
modification_date
</string>
</key>
<value>
<object>
<klass>
<global
name=
"DateTime"
module=
"DateTime.DateTime"
/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>
1455284352.14
</float>
<string>
UTC
</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Contact List
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_js.js
0 → 100644
View file @
edcd20f8
/*globals window, RSVP, rJS*/
/*jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80*/
(
function
(
window
,
RSVP
,
rJS
)
{
'
use strict
'
;
/* Settings required:
* - jio_storage_description, or be redirected
*/
// XXX add more customization to allow reuse across different portal types
rJS
(
window
)
// Neither state to set nor ready to initialize
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
'
updateHeader
'
,
'
updateHeader
'
)
.
declareAcquiredMethod
(
'
redirect
'
,
'
redirect
'
)
.
declareAcquiredMethod
(
'
getSetting
'
,
'
getSetting
'
)
.
declareAcquiredMethod
(
'
requireSetting
'
,
'
requireSetting
'
)
.
declareAcquiredMethod
(
'
jio_post
'
,
'
jio_post
'
)
/* Render the gadget
* Parameters:
* - get setting: jio_storage_description
* Returns: nothing
* Effects:
* - update header, page title to 'Contact List'
* - call listbox.render hardcoded, not jio_general_query
* - redirect if no jIO storage available
*/
.
declareMethod
(
'
render
'
,
function
()
{
const
gadget
=
this
;
return
gadget
.
requireSetting
(
'
jio_storage_description
'
,
'
jio_configurator
'
,
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
updateHeader
({
page_title
:
'
Contact List
'
,
add_action
:
true
,
});
})
.
push
(
function
()
{
return
gadget
.
getDeclaredGadget
(
'
listbox
'
);
})
.
push
(
function
(
listbox
)
{
return
listbox
.
render
({
gadget_title
:
'
Contact List
'
,
gadget_query
:
{
limit
:
[
0
,
1000000
],
query
:
'
portal_type: "Person"
'
,
sort_on
:
[
[
'
jio_configuration
'
,
'
descending
'
],
[
'
modification_date
'
,
'
descending
'
],
],
select_list
:
[
'
jio_configuration
'
,
'
default_email_coordinate_text
'
,
'
first_name
'
,
'
last_name
'
,
],
},
column_list
:
[{
select
:
'
default_email_coordinate_text
'
,
title
:
'
Email
'
,
},
{
select
:
'
last_name
'
,
title
:
'
Last Name
'
,
},
{
select
:
'
first_name
'
,
title
:
'
First Name
'
,
}],
});
})
);
})
/* Add a new contact when the right button is clicked
* Parameters: nothing
* Returns: nothing
* Effects:
* - post a new contact to jIO storage
* - redirect to the contact view page
*/
.
declareMethod
(
'
triggerSubmit
'
,
function
()
{
const
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
jio_post
({
parent_relative_url
:
'
person_module
'
,
portal_type
:
'
Person
'
,
});
})
.
push
(
function
(
id
)
{
return
gadget
.
redirect
({
command
:
'
display
'
,
options
:
{
jio_key
:
id
,
page
:
'
jio_person_view
'
,
},
});
});
});
}(
window
,
RSVP
,
rJS
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_contact_list_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
creators
</string>
</key>
<value>
<tuple>
<string>
cedric.le.ninivin
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_list.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_contact_list_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
modification_date
</string>
</key>
<value>
<object>
<klass>
<global
name=
"DateTime"
module=
"DateTime.DateTime"
/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>
1455284352.18
</float>
<string>
UTC
</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Contact List JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, user-scalable=no"
>
<title>
jIO DAV Configurator Gadget
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"gadget_erp5_page_jio_dav_configurator.js"
></script>
</head>
<body>
<article
class=
"configurator-erp5"
>
<header
class=
"configurator-header"
>
<h3>
<span
class=
"fa fa-database"
aria-hidden=
"true"
></span>
Your WebDAV Parameters
</h3>
</header>
<section
class=
"configurator-section"
>
<form>
<label>
Connection URL:
</label>
<input
type=
"url"
name=
"dav_url"
required=
"required"
placeholder=
"https://softinst75722.host.vifib.net/share"
/>
<label>
WebDAV username:
</label>
<input
type=
"text"
name=
"dav_user"
required=
"required"
placeholder=
"eyqs"
/>
<label>
WebDAV password:
</label>
<input
type=
"password"
name=
"dav_pass"
required=
"required"
placeholder=
"correct horse battery staple"
/>
<button
class=
"grid-box configurator-button"
type=
"submit"
>
Connect
</button>
</form>
</section>
</article>
<div
data-gadget-url=
"gadget_erp5_page_jio_document_configurator.html"
data-gadget-scope=
"document_configurator"
data-gadget-sandbox=
"public"
></div>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_dav_configurator.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_dav_configurator_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
jIO DAV Configurator Gadget
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_js.js
0 → 100644
View file @
edcd20f8
/* global window, RSVP, rJS, btoa */
/* jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80 */
(
function
(
window
,
RSVP
,
rJS
,
btoa
)
{
'
use strict
'
;
/* Settings required:
* - jio_general_query
* - user_email, or be redirected
* Settings used:
* - connect_page, default: ''
* - indexeddb_database_name, default: 'jio'
* Settings modified:
* - jio_storage_name, to: 'dav'
* - jio_storage_description
* - redirect_after_reload, to: {command: 'display', options: {page: 'sync'}}
*/
/* Generate a generic jIO configuration for WebDAV storage
* Parameters:
* - dav_url: the WebDAV URL from the configuration form,
* example: https://softinst75722.host.vifib.net/share
* - dav_user: the WebDAV username, example: eyqs
* - dav_pass: the WebDAV password, example: correct horse battery staple
* - get setting: jio_general_query, indexeddb_database_name
* Effects:
* - set setting:
* jio_storage_name, jio_storage_description, redirect_after_reload
* - reload, then redirect to sync
*/
function
setDavConfiguration
(
gadget
,
event
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
setSetting
(
'
jio_storage_name
'
,
'
dav
'
);
})
.
push
(
function
()
{
return
RSVP
.
all
([
gadget
.
getSetting
(
'
jio_general_query
'
),
gadget
.
getSetting
(
'
indexeddb_database_name
'
,
'
jio
'
),
]);
})
.
push
(
function
(
setting_list
)
{
const
dav_url
=
event
.
target
.
dav_url
.
value
;
const
dav_user
=
event
.
target
.
dav_user
.
value
;
const
dav_pass
=
event
.
target
.
dav_pass
.
value
;
const
configuration
=
{
type
:
'
replicate
'
,
query
:
{
query
:
setting_list
[
0
],
limit
:
[
0
,
16777215
],
},
use_remote_post
:
true
,
conflict_handling
:
2
,
check_local_deletion
:
false
,
local_sub_storage
:
{
type
:
'
query
'
,
sub_storage
:
{
type
:
'
uuid
'
,
sub_storage
:
{
type
:
'
indexeddb
'
,
database
:
setting_list
[
1
],
},
},
},
remote_sub_storage
:
{
type
:
'
query
'
,
sub_storage
:
{
type
:
'
drivetojiomapping
'
,
sub_storage
:
{
type
:
'
dav
'
,
url
:
dav_url
,
basic_login
:
btoa
(
dav_user
+
'
:
'
+
dav_pass
),
with_credentials
:
true
,
},
},
},
};
return
RSVP
.
all
([
gadget
.
setSetting
(
'
jio_storage_description
'
,
configuration
),
gadget
.
setSetting
(
'
redirect_after_reload
'
,
{
command
:
'
display
'
,
options
:
{
page
:
'
sync
'
},
})
]);
})
.
push
(
function
()
{
return
gadget
.
reload
();
})
}
rJS
(
window
)
// Neither state to set nor ready to initialize
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
'
updateHeader
'
,
'
updateHeader
'
)
.
declareAcquiredMethod
(
'
redirect
'
,
'
redirect
'
)
.
declareAcquiredMethod
(
'
reload
'
,
'
reload
'
)
.
declareAcquiredMethod
(
'
setSetting
'
,
'
setSetting
'
)
.
declareAcquiredMethod
(
'
getSetting
'
,
'
getSetting
'
)
.
declareAcquiredMethod
(
'
requireSetting
'
,
'
requireSetting
'
)
/* Render the gadget
* Parameters:
* - get setting: jio_storage_name, jio_storage_description, user_email
* Effects:
* - update header, page title to 'Configure WebDAV Storage'
* - hide document_configurator gadget based on jio_storage_name
* - pre-fill input field if jio_storage_description already defined
* - redirect if user not logged in
*/
.
declareMethod
(
'
render
'
,
function
()
{
const
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getSetting
(
'
connect_page
'
,
'
none
'
);
})
.
push
(
function
(
connect_page
)
{
return
gadget
.
requireSetting
(
'
user_email
'
,
connect_page
,
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
updateHeader
({
page_title
:
'
Configure WebDAV Storage
'
,
back_url
:
'
#!display?n.page=jio_configurator
'
,
});
})
.
push
(
function
()
{
return
gadget
.
getSetting
(
'
jio_storage_name
'
,
'
none
'
);
})
.
push
(
function
(
storage_name
)
{
if
(
storage_name
===
'
dav
'
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getSetting
(
'
jio_storage_description
'
);
})
.
push
(
function
(
configuration
)
{
gadget
.
element
.
querySelector
(
'
input[name="dav_url"]
'
)
.
value
=
configuration
.
remote_sub_storage
.
sub_storage
.
sub_storage
.
url
;
return
;
});
}
else
{
gadget
.
element
.
querySelector
(
'
div[data-gadget-scope=
'
+
'
document_configurator
'
).
style
.
display
=
'
none
'
;
}
return
;
})
);
});
})
// Call setDavConfiguration when the form is submitted
.
onEvent
(
'
submit
'
,
function
(
event
)
{
const
gadget
=
this
;
return
setDavConfiguration
(
gadget
,
event
);
});
}(
window
,
RSVP
,
rJS
,
btoa
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_dav_configurator_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_dav_configurator.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_dav_configurator_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
jiO DAV Configurator Gadget JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_document_configurator_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, user-scalable=no"
>
<title>
jIO Configurator Document Gadget
</title>
</head>
<body>
<article
class=
"configurator-document"
>
<header
class=
"configurator-header"
>
<h3>
<span
class=
"fa fa-file-text-o"
aria-hidden=
"true"
></span>
Your Documents
</h3>
</header>
<section
class=
"configurator-section"
>
<p>
</p>
<p
class=
"configurator-command"
>
Continue by
<strong>
synchronizing
</strong>
with your remote storage or
<strong>
accessing
</strong>
your documents.
</p>
<div
class=
"configurator-grid"
>
<a
class=
"grid-box configurator-button"
href=
"#!display?n.page=sync"
>
Synchronize
</a>
<a
class=
"grid-box configurator-button"
href=
"#!display?n.page=contact_list"
>
Access Document List
</a>
</div>
</section>
</article>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_document_configurator_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_document_configurator.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_document_configurator_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<string>
en
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
jIO Document Configurator Gadget
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
001
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, user-scalable=no"
>
<title>
jIO ERP5 Configurator Gadget
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"gadget_erp5_page_jio_erp5_configurator.js"
></script>
</head>
<body>
<article
class=
"configurator-erp5"
>
<header
class=
"configurator-header"
>
<h3>
<span
class=
"fa fa-database"
aria-hidden=
"true"
></span>
Your ERP5 Parameters
</h3>
</header>
<section
class=
"configurator-section"
>
<p>
You must already be logged into the following ERP5 instance before you synchronize!
</p>
<form>
<label>
Connection URL:
</label>
<input
type=
"url"
name=
"erp5_url"
required=
"required"
placeholder=
"https://nexedi.erp5.net/web_site_module/"
>
<button
class=
"grid-box configurator-button"
type=
"submit"
>
Connect
</button>
</form>
</section>
</article>
<div
data-gadget-url=
"gadget_erp5_page_jio_document_configurator.html"
data-gadget-scope=
"document_configurator"
data-gadget-sandbox=
"public"
></div>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_erp5_configurator.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_erp5_configurator_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<string>
en
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
jIO ERP5 Configurator Gadget
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
001
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_js.js
0 → 100644
View file @
edcd20f8
/* global window, RSVP, rJS, URI */
/* jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80 */
(
function
(
window
,
RSVP
,
rJS
,
URI
)
{
'
use strict
'
;
/* Settings required:
* - either erp5_jio_query or jio_general_query
* - user_email, or be redirected
* Settings used:
* - connect_page, default: ''
* - indexeddb_database_name, default: 'jio'
* Settings modified:
* - jio_storage_name, to: 'erp5'
* - jio_storage_description
* - redirect_after_reload, to: {command: 'display', options: {page: 'sync'}}
*/
/* Generate a generic jIO configuration for ERP5 storage
* Parameters:
* - erp5_url: the ERP5 URL from the configuration form,
* example: https://softinst75770.host.vifib.net/web_page_module/
* - get setting: jio_erp5_query, jio_general_query, indexeddb_database_name
* Effects:
* - set setting:
* jio_storage_name, jio_storage_description, redirect_after_reload
* - reload, then redirect to sync
*/
function
setErp5Configuration
(
gadget
,
event
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
setSetting
(
'
jio_storage_name
'
,
'
erp5
'
);
})
.
push
(
function
()
{
return
gadget
.
getSetting
(
'
jio_general_query
'
,
'
none
'
);
})
.
push
(
function
(
general_query
)
{
if
(
general_query
===
'
none
'
)
{
general_query
=
null
;
}
return
RSVP
.
all
([
gadget
.
getSetting
(
'
jio_erp5_query
'
,
general_query
),
gadget
.
getSetting
(
'
indexeddb_database_name
'
,
'
jio
'
),
]);
})
.
push
(
function
(
setting_list
)
{
const
erp5_url
=
event
.
target
.
erp5_url
.
value
;
const
configuration
=
{
type
:
'
replicate
'
,
query
:
{
query
:
setting_list
[
0
],
limit
:
[
0
,
16777215
],
},
use_remote_post
:
true
,
conflict_handling
:
2
,
check_local_deletion
:
false
,
local_sub_storage
:
{
type
:
'
query
'
,
sub_storage
:
{
type
:
'
uuid
'
,
sub_storage
:
{
type
:
'
indexeddb
'
,
database
:
setting_list
[
1
],
},
},
},
remote_sub_storage
:
{
type
:
'
erp5
'
,
erp5_url
:
erp5_url
,
url
:
(
new
URI
(
'
hateoas
'
)).
absoluteTo
(
erp5_url
).
toString
(),
default_view_reference
:
'
jio_view
'
,
},
};
return
RSVP
.
all
([
gadget
.
setSetting
(
'
jio_storage_description
'
,
configuration
),
gadget
.
setSetting
(
'
redirect_after_reload
'
,
{
command
:
'
display
'
,
options
:
{
page
:
'
sync
'
},
})
]);
})
.
push
(
function
()
{
return
gadget
.
reload
();
})
}
rJS
(
window
)
// Neither state to set nor ready to initialize
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
'
updateHeader
'
,
'
updateHeader
'
)
.
declareAcquiredMethod
(
'
redirect
'
,
'
redirect
'
)
.
declareAcquiredMethod
(
'
reload
'
,
'
reload
'
)
.
declareAcquiredMethod
(
'
setSetting
'
,
'
setSetting
'
)
.
declareAcquiredMethod
(
'
getSetting
'
,
'
getSetting
'
)
.
declareAcquiredMethod
(
'
requireSetting
'
,
'
requireSetting
'
)
/* Render the gadget
* Parameters:
* - get setting: jio_storage_name, jio_storage_description, user_email
* Effects:
* - update header, page title to 'Configure ERP5 Storage'
* - hide document_configurator gadget based on jio_storage_name
* - pre-fill input field if jio_storage_description already defined
* - redirect if user not logged in
*/
.
declareMethod
(
'
render
'
,
function
()
{
const
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getSetting
(
'
connect_page
'
,
'
none
'
);
})
.
push
(
function
(
connect_page
)
{
return
gadget
.
requireSetting
(
'
user_email
'
,
connect_page
,
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
updateHeader
({
page_title
:
'
Configure ERP5 Storage
'
,
back_url
:
'
#!display?n.page=jio_configurator
'
,
});
})
.
push
(
function
()
{
return
gadget
.
getSetting
(
'
jio_storage_name
'
,
'
none
'
);
})
.
push
(
function
(
storage_name
)
{
if
(
storage_name
===
'
erp5
'
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
getSetting
(
'
jio_storage_description
'
);
})
.
push
(
function
(
configuration
)
{
gadget
.
element
.
querySelector
(
'
input[name="erp5_url"]
'
)
.
value
=
configuration
.
remote_sub_storage
.
erp5_url
;
return
;
});
}
else
{
gadget
.
element
.
querySelector
(
'
div[data-gadget-scope=
'
+
'
document_configurator
'
).
style
.
display
=
'
none
'
;
}
return
;
})
);
});
})
// Call setErp5Configuration when the form is submitted
.
onEvent
(
'
submit
'
,
function
(
event
)
{
const
gadget
=
this
;
return
setErp5Configuration
(
gadget
,
event
);
});
}(
window
,
RSVP
,
rJS
,
URI
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_erp5_configurator_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_erp5_configurator.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_erp5_configurator_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<string>
en
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
jIO ERP5 Configurator Gadget JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<string>
001
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
<title>
OfficeJS Contact View
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"handlebars.js"
></script>
<script
src=
"gadget_erp5_page_jio_person_view.js"
></script>
<script
class=
"contact_template"
type=
"text/x-handlebars-template"
>
<
form
class
=
"
contact-form
"
>
<
label
>
E
-
mail
Address
:
<
/label
>
<
input
type
=
"
text
"
name
=
"
default_email_coordinate_text
"
value
=
"
{{default_email_coordinate_text}}
"
/>
<
label
>
Last
Name
:
<
/label
>
<
input
type
=
"
text
"
name
=
"
last_name
"
value
=
"
{{last_name}}
"
/>
<
label
>
First
Name
:
<
/label
>
<
input
type
=
"
text
"
name
=
"
first_name
"
value
=
"
{{first_name}}
"
/>
<
label
>
jIO
Configuration
:
<
/label
>
<
input
type
=
"
text
"
name
=
"
jio_configuration
"
value
=
"
{{jio_configuration}}
"
/>
<
/form
>
</script>
</head>
<body>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_person_view.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_person_view_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
ERP5 jIO Person View
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_js.js
0 → 100644
View file @
edcd20f8
/*globals window, rJS, Handlebars, RSVP, loopEventListener, console*/
/*jslint indent: 2, nomen: true, maxlen: 80*/
(
function
(
window
,
RSVP
,
rJS
,
Handlebars
)
{
'
use strict
'
;
/* Settings required:
* - jio_storage_description, or be redirected
*/
// Precompile Handlebars templates while loading the first gadget instance
const
gadget_klass
=
rJS
(
window
);
const
contact_template
=
Handlebars
.
compile
(
gadget_klass
.
__template_element
.
querySelector
(
'
.contact_template
'
).
innerHTML
);
gadget_klass
.
setState
({
// Document metadata to store in jIO
doc
:
null
,
jio_key
:
null
,
})
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
'
updateHeader
'
,
'
updateHeader
'
)
.
declareAcquiredMethod
(
'
redirect
'
,
'
redirect
'
)
.
declareAcquiredMethod
(
'
translateHtml
'
,
'
translateHtml
'
)
.
declareAcquiredMethod
(
'
requireSetting
'
,
'
requireSetting
'
)
.
declareAcquiredMethod
(
'
jio_allDocs
'
,
'
jio_allDocs
'
)
.
declareAcquiredMethod
(
'
jio_put
'
,
'
jio_put
'
)
.
declareAcquiredMethod
(
'
jio_get
'
,
'
jio_get
'
)
/* Render the gadget
* Parameters: all in option_dict,
* - jio_key: the id of the document in the jIO storage
* - get setting: jio_storage_description
* Effects:
* - jIO get: the contact info associated with jio_key
* - change state: add the contact info from jIO
* - run the contact info through Handlebars to render the forms
* - redirect if no jIO storage available
*/
.
declareMethod
(
'
render
'
,
function
(
option_dict
)
{
const
gadget
=
this
;
return
gadget
.
requireSetting
(
'
jio_storage_description
'
,
'
jio_configurator
'
,
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
jio_get
(
option_dict
.
jio_key
);
})
.
push
(
function
(
result
)
{
return
gadget
.
changeState
({
jio_key
:
option_dict
.
jio_key
,
doc
:
result
});
})
.
push
(
function
()
{
return
gadget
.
translateHtml
(
contact_template
(
gadget
.
state
.
doc
));
})
.
push
(
function
(
translated_html
)
{
gadget
.
element
.
innerHTML
=
translated_html
;
let
title
;
if
(
gadget
.
state
.
doc
.
default_email_coordinate_text
)
{
title
=
gadget
.
state
.
doc
.
default_email_coordinate_text
+
'
| Contact
'
;
}
else
{
title
=
'
Anonymous Contact
'
;
}
return
gadget
.
updateHeader
({
page_title
:
title
,
save_action
:
true
,
});
})
);
})
/* Save current contact information when the right button is clicked
* Parameters:
* - the current values from the contact form
* Effects:
* - jIO put: the contact info from the contact form
* - redirect to the same page for a visual indication of success
*/
.
declareMethod
(
'
triggerSubmit
'
,
function
()
{
const
gadget
=
this
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
const
fields
=
gadget
.
element
.
querySelector
(
'
.contact-form
'
).
elements
;
for
(
let
i
=
0
,
i_len
=
fields
.
length
;
i
<
i_len
;
i
++
)
{
gadget
.
state
.
doc
[
fields
[
i
].
name
]
=
fields
[
i
].
value
;
}
// ERP5: can not store property: modification_date
// gadget.state.doc.modification_date = new Date().toISOString();
})
.
push
(
function
()
{
return
gadget
.
jio_put
(
gadget
.
state
.
jio_key
,
gadget
.
state
.
doc
);
})
.
push
(
function
()
{
return
gadget
.
redirect
({
command
:
'
display
'
,
options
:
{
jio_key
:
gadget
.
state
.
jio_key
,
page
:
'
jio_person_view
'
,
},
});
});
});
}(
window
,
RSVP
,
rJS
,
Handlebars
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_jio_person_view_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_person_view.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_jio_person_view_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
ERP5 jIO Person View JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_html.html
0 → 100644
View file @
edcd20f8
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, user-scalable=no"
>
<title>
jIO Configurator Gadget
</title>
<script
src=
"rsvp.js"
></script>
<script
src=
"renderjs.js"
></script>
<script
src=
"handlebars.js"
></script>
<script
src=
"gadget_erp5_page_sync.js"
></script>
</head>
<body>
<article
class=
"configurator-sync"
>
<header
class=
"configurator-header"
>
<h3>
<span
class=
"fa fa-refresh"
aria-hidden=
"true"
></span>
Synchronize Your Data
</h3>
</header>
<form>
<button
class=
"configurator-button"
type=
"submit"
>
Launch Synchronization
</button>
</form>
</article>
</body>
</html>
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_html.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Page"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_sync.html
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_sync_html
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Page
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Sync Page
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_js.js
0 → 100644
View file @
edcd20f8
/* global window, RSVP, rJS */
/* jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80 */
(
function
(
window
,
RSVP
,
rJS
)
{
'
use strict
'
;
/* Settings required:
* - jio_storage_description, or be redirected
*/
/* Synchronize between the local and remote storages in replicate storage
* Parameters: nothing
* Returns: nothing
* Effects:
* - repair replicate storage
* - redirect to sync
*/
function
launchSynchronization
(
gadget
,
event
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
event
.
target
.
elements
[
0
].
disabled
=
true
;
return
gadget
.
jio_repair
();
})
.
push
(
function
(
result_dict
)
{
if
(
result_dict
&&
result_dict
.
hasOwnProperty
(
'
redirect
'
))
{
return
gadget
.
redirect
({
command
:
'
display
'
,
options
:
{
page
:
result_dict
.
redirect
},
});
}
else
{
return
gadget
.
redirect
();
}
});
}
rJS
(
window
)
// Neither state to set nor ready to initialize
// The following functions are all acquired from erp5_launcher_nojqm.js
.
declareAcquiredMethod
(
'
updateHeader
'
,
'
updateHeader
'
)
.
declareAcquiredMethod
(
'
redirect
'
,
'
redirect
'
)
.
declareAcquiredMethod
(
'
jio_repair
'
,
'
jio_repair
'
)
.
declareAcquiredMethod
(
'
requireSetting
'
,
'
requireSetting
'
)
/* Render the gadget
* Parameters:
* - get setting: jio_storage_description
* Effects:
* - update header, page_title to 'Synchronize'
* - redirect if no jIO storage available
*/
.
declareMethod
(
'
render
'
,
function
()
{
const
gadget
=
this
;
return
gadget
.
requireSetting
(
'
jio_storage_description
'
,
'
jio_configurator
'
,
new
RSVP
.
Queue
()
.
push
(
function
()
{
return
gadget
.
updateHeader
({
page_title
:
'
Synchronize
'
});
})
);
})
// Call launchSynchronization when the form is submitted
.
onEvent
(
'
submit
'
,
function
(
event
)
{
const
gadget
=
this
;
return
launchSynchronization
(
gadget
,
event
);
});
}(
window
,
RSVP
,
rJS
));
\ No newline at end of file
bt5/officejs_eyqs/PathTemplateItem/web_page_module/gadget_erp5_page_sync_js.xml
0 → 100644
View file @
edcd20f8
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Web Script"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Access_contents_information_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Add_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Change_local_roles_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_Modify_portal_content_Permission
</string>
</key>
<value>
<tuple>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Manager
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
_View_Permission
</string>
</key>
<value>
<tuple>
<string>
Anonymous
</string>
<string>
Assignee
</string>
<string>
Assignor
</string>
<string>
Associate
</string>
<string>
Auditor
</string>
<string>
Manager
</string>
<string>
Owner
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
classification/collaborative/team
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
content_md5
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
default_reference
</string>
</key>
<value>
<string>
gadget_erp5_page_sync.js
</string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
gadget_erp5_page_sync_js
</string>
</value>
</item>
<item>
<key>
<string>
language
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Web Script
</string>
</value>
</item>
<item>
<key>
<string>
short_title
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
OfficeJS Sync Page JS
</string>
</value>
</item>
<item>
<key>
<string>
version
</string>
</key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/officejs_eyqs/bt/dependency_list
0 → 100644
View file @
edcd20f8
erp5_base
erp5_web
erp5_web_renderjs_ui
\ No newline at end of file
bt5/officejs_eyqs/bt/maintainer_list
0 → 100644
View file @
edcd20f8
eugene.shen
\ No newline at end of file
bt5/officejs_eyqs/bt/template_format_version
0 → 100644
View file @
edcd20f8
1
\ No newline at end of file
bt5/officejs_eyqs/bt/template_path_list
0 → 100644
View file @
edcd20f8
web_page_module/adapter_js
web_page_module/erp5_page_launcher*
web_page_module/fast_priority_queue_js
web_page_module/gadget_erp5_chat_panel_*
web_page_module/gadget_erp5_chat_webrtc_*
web_page_module/gadget_erp5_nojquery_css
web_page_module/gadget_erp5_page_chat_*
web_page_module/gadget_erp5_page_contact_*
web_page_module/gadget_erp5_page_field_listbox_widget_*
web_page_module/gadget_erp5_page_jio_*_configurator_*
web_page_module/gadget_erp5_page_jio_person_view_*
web_page_module/gadget_erp5_page_sync_*
\ No newline at end of file
bt5/officejs_eyqs/bt/title
0 → 100644
View file @
edcd20f8
officejs_eyqs
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment