Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F256710
files_api.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
33 KB
Referenced Files
None
Subscribers
None
files_api.js
View Options
/**
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
| Copyright (C) 2012-2015, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
function
files_api
()
{
var
ref
=
this
;
// default config
this
.
sessions
=
{};
this
.
translations
=
{};
this
.
env
=
{
url
:
'api/'
,
directory_separator
:
'/'
,
resources_dir
:
'resources'
};
/*********************************************************/
/********* Basic utilities *********/
/*********************************************************/
// set environment variable(s)
this
.
set_env
=
function
(
p
,
value
)
{
if
(
p
!=
null
&&
typeof
p
===
'object'
&&
!
value
)
for
(
var
n
in
p
)
this
.
env
[
n
]
=
p
[
n
];
else
this
.
env
[
p
]
=
value
;
};
// add a localized label(s) to the client environment
this
.
tdef
=
function
(
p
,
value
)
{
if
(
typeof
p
==
'string'
)
this
.
translations
[
p
]
=
value
;
else
if
(
typeof
p
==
'object'
)
$
.
extend
(
this
.
translations
,
p
);
};
// return a localized string
this
.
t
=
function
(
label
)
{
if
(
this
.
translations
[
label
])
return
this
.
translations
[
label
];
else
return
label
;
};
/********************************************************/
/********* Remote request methods *********/
/********************************************************/
// send a http POST request to the API service
this
.
post
=
function
(
action
,
data
,
func
)
{
var
url
=
this
.
env
.
url
+
'?method='
+
action
;
if
(
!
func
)
func
=
'response'
;
this
.
set_request_time
();
return
$
.
ajax
({
type
:
'POST'
,
url
:
url
,
data
:
JSON
.
stringify
(
data
),
dataType
:
'json'
,
contentType
:
'application/json; charset=utf-8'
,
success
:
function
(
response
)
{
if
(
typeof
func
==
'function'
)
func
(
response
);
else
ref
[
func
](
response
);
},
error
:
function
(
o
,
status
,
err
)
{
ref
.
http_error
(
o
,
status
,
err
,
data
);
},
cache
:
false
,
beforeSend
:
function
(
xmlhttp
)
{
xmlhttp
.
setRequestHeader
(
'X-Session-Token'
,
ref
.
env
.
token
);
}
});
};
// send a http GET request to the API service
this
.
get
=
function
(
action
,
data
,
func
)
{
var
url
=
this
.
env
.
url
;
if
(
!
func
)
func
=
'response'
;
this
.
set_request_time
();
data
.
method
=
action
;
return
$
.
ajax
({
type
:
'GET'
,
url
:
url
,
data
:
data
,
dataType
:
'json'
,
success
:
function
(
response
)
{
if
(
typeof
func
==
'function'
)
func
(
response
);
else
ref
[
func
](
response
);
},
error
:
function
(
o
,
status
,
err
)
{
ref
.
http_error
(
o
,
status
,
err
,
data
);
},
cache
:
false
,
beforeSend
:
function
(
xmlhttp
)
{
xmlhttp
.
setRequestHeader
(
'X-Session-Token'
,
ref
.
env
.
token
);
}
});
};
// send request with auto-selection of POST/GET method
this
.
request
=
function
(
action
,
data
,
func
)
{
// Use POST for modification actions with probable big request size
var
method
=
/_(create|delete|move|copy|update|auth|subscribe|unsubscribe|invite|decline|request|accept|remove)$/
.
test
(
action
)
?
'post'
:
'get'
;
return
this
[
method
](
action
,
data
,
func
);
};
// handle HTTP request errors
this
.
http_error
=
function
(
request
,
status
,
err
,
data
)
{
var
errmsg
=
request
.
statusText
;
this
.
set_busy
(
false
);
request
.
abort
();
if
(
request
.
status
&&
errmsg
)
this
.
display_message
(
this
.
t
(
'servererror'
)
+
' ('
+
errmsg
+
')'
,
'error'
);
};
this
.
response
=
function
(
response
)
{
this
.
update_request_time
();
this
.
set_busy
(
false
);
return
this
.
response_parse
(
response
);
};
this
.
response_parse
=
function
(
response
)
{
if
(
!
response
||
response
.
status
!=
'OK'
)
{
// Logout on invalid-session error
if
(
response
&&
response
.
code
==
403
)
this
.
logout
(
response
);
else
this
.
display_message
(
response
&&
response
.
reason
?
response
.
reason
:
this
.
t
(
'servererror'
),
'error'
);
return
false
;
}
return
true
;
};
/*********************************************************/
/********* Utilities *********/
/*********************************************************/
// Called on "session expired" session
this
.
logout
=
function
(
response
)
{};
// set state
this
.
set_busy
=
function
(
state
,
message
)
{};
// displays error message
this
.
display_message
=
function
(
label
,
type
)
{};
// called when a request timed out
this
.
request_timed_out
=
function
()
{};
// called on start of the request
this
.
set_request_time
=
function
()
{};
// called on request response
this
.
update_request_time
=
function
()
{};
/*********************************************************/
/********* Helpers *********/
/*********************************************************/
// compose a valid url with the given parameters
this
.
url
=
function
(
action
,
query
)
{
var
k
,
param
=
{},
querystring
=
typeof
query
===
'string'
?
'&'
+
query
:
''
;
if
(
typeof
action
!==
'string'
)
query
=
action
;
else
if
(
!
query
||
typeof
query
!==
'object'
)
query
=
{};
// overwrite task name
if
(
action
)
query
.
method
=
action
;
// remove undefined values
for
(
k
in
query
)
{
if
(
query
[
k
]
!==
undefined
&&
query
[
k
]
!==
null
)
param
[
k
]
=
query
[
k
];
}
return
'?'
+
$
.
param
(
param
)
+
querystring
;
};
// fill folder selector with options
this
.
folder_select_element
=
function
(
select
,
params
)
{
var
options
=
[],
selected
=
params
&&
params
.
selected
?
params
.
selected
:
this
.
env
.
folder
;
if
(
params
&&
params
.
empty
)
options
.
push
(
$
(
'<option>'
).
val
(
''
).
text
(
'---'
));
$
.
each
(
this
.
env
.
folders
,
function
(
i
,
f
)
{
var
n
,
name
=
escapeHTML
(
f
.
name
);
// skip read-only folders
if
(
params
&&
params
.
writable
&&
(
f
.
readonly
||
f
.
virtual
))
{
var
folder
,
found
=
false
,
prefix
=
i
+
ref
.
env
.
directory_separator
;
// for virtual folders check if there's any writable subfolder
for
(
n
in
ref
.
env
.
folders
)
{
if
(
n
.
indexOf
(
prefix
)
===
0
)
{
folder
=
ref
.
env
.
folders
[
n
];
if
(
!
folder
.
virtual
&&
!
folder
.
readonly
)
{
found
=
true
;
break
;
}
}
}
if
(
!
found
)
return
;
}
for
(
n
=
0
;
n
<
f
.
depth
;
n
++
)
name
=
' '
+
name
;
options
.
push
(
$
(
'<option>'
).
val
(
i
).
html
(
name
));
});
select
.
empty
().
append
(
options
);
if
(
selected
)
select
.
val
(
selected
);
};
// Folder list parser, converts it into structure
this
.
folder_list_parse
=
function
(
list
,
num
,
subscribed
)
{
var
i
,
n
,
j
,
items
,
items_len
,
f
,
tmp
,
folder
,
readonly
,
subs_support
,
subs_prefixes
=
{},
found
,
separator
=
this
.
env
.
directory_separator
,
len
=
list
?
list
.
length
:
0
,
folders
=
{};
if
(
!
num
)
num
=
1
;
if
(
subscribed
===
undefined
)
subscribed
=
true
;
// prepare subscriptions support detection
if
(
len
&&
this
.
env
.
caps
)
{
subs_support
=
!!
this
.
env
.
caps
.
SUBSCRIPTIONS
;
$
.
each
(
this
.
env
.
caps
.
MOUNTPOINTS
||
[],
function
(
i
,
v
)
{
subs_prefixes
[
i
]
=
!!
v
.
SUBSCRIPTIONS
;
});
}
for
(
i
=
0
;
i
<
len
;
i
++
)
{
folder
=
list
[
i
];
readonly
=
false
;
// in extended format folder is an object
if
(
typeof
folder
!==
'string'
)
{
readonly
=
folder
.
readonly
;
folder
=
folder
.
folder
;
}
items
=
folder
.
split
(
separator
);
items_len
=
items
.
length
;
for
(
n
=
0
;
n
<
items_len
-
1
;
n
++
)
{
tmp
=
items
.
slice
(
0
,
n
+
1
);
f
=
tmp
.
join
(
separator
);
if
(
!
folders
[
f
])
folders
[
f
]
=
{
name
:
tmp
.
pop
(),
depth
:
n
,
id
:
'f'
+
num
++
,
virtual
:
1
};
}
folders
[
folder
]
=
{
name
:
items
.
pop
(),
depth
:
items_len
-
1
,
id
:
'f'
+
num
++
,
readonly
:
readonly
};
// set subscription flag, leave undefined if the source does not support subscriptions
found
=
false
;
for
(
j
in
subs_prefixes
)
{
if
(
folder
===
j
)
{
// this is a mount point
found
=
true
;
break
;
}
if
(
folder
.
indexOf
(
j
+
separator
)
===
0
)
{
if
(
subs_prefixes
[
j
])
folders
[
folder
].
subscribed
=
subscribed
;
found
=
true
;
break
;
}
}
if
(
!
found
&&
subs_support
)
folders
[
folder
].
subscribed
=
subscribed
;
}
return
folders
;
};
// folder structure presentation (structure icons)
this
.
folder_list_tree
=
function
(
folders
)
{
var
i
,
n
,
diff
,
prefix
,
tree
=
[],
folder
;
for
(
i
in
folders
)
{
items
=
i
.
split
(
this
.
env
.
directory_separator
);
items_len
=
items
.
length
;
// skip root
if
(
items_len
<
2
)
{
tree
=
[];
continue
;
}
folders
[
i
].
tree
=
[
1
];
prefix
=
items
.
slice
(
0
,
items_len
-
1
).
join
(
this
.
env
.
directory_separator
)
+
this
.
env
.
directory_separator
;
for
(
n
=
0
;
n
<
tree
.
length
;
n
++
)
{
folder
=
tree
[
n
];
diff
=
folders
[
folder
].
depth
-
(
items_len
-
1
);
if
(
diff
>=
0
&&
folder
.
indexOf
(
prefix
)
===
0
)
folders
[
folder
].
tree
[
diff
]
|=
2
;
}
tree
.
push
(
i
);
}
for
(
i
in
folders
)
{
if
(
tree
=
folders
[
i
].
tree
)
{
var
html
=
''
,
divs
=
[];
for
(
n
=
0
;
n
<
folders
[
i
].
depth
;
n
++
)
{
if
(
tree
[
n
]
>
2
)
divs
.
push
({
'class'
:
'l3'
,
width
:
15
});
else
if
(
tree
[
n
]
>
1
)
divs
.
push
({
'class'
:
'l2'
,
width
:
15
});
else
if
(
tree
[
n
]
>
0
)
divs
.
push
({
'class'
:
'l1'
,
width
:
15
});
// separator
else
if
(
divs
.
length
&&
!
divs
[
divs
.
length
-
1
][
'class'
])
divs
[
divs
.
length
-
1
].
width
+=
15
;
else
divs
.
push
({
'class'
:
null
,
width
:
15
});
}
for
(
n
=
divs
.
length
-
1
;
n
>=
0
;
n
--
)
{
if
(
divs
[
n
][
'class'
])
html
+=
'<span class="tree '
+
divs
[
n
][
'class'
]
+
'" />'
;
else
html
+=
'<span style="width:'
+
divs
[
n
].
width
+
'px" />'
;
}
if
(
html
)
$
(
'#'
+
folders
[
i
].
id
+
' span.branch'
).
html
(
html
);
}
}
};
// Get editing sessions on the specified file
this
.
file_sessions
=
function
(
file
)
{
var
sessions
=
[],
folder
=
this
.
file_path
(
file
);
$
.
each
(
this
.
sessions
[
folder
]
||
{},
function
(
session_id
,
session
)
{
if
(
session
.
file
==
file
)
{
session
.
id
=
session_id
;
sessions
.
push
(
session
);
}
});
return
sessions
;
};
// convert content-type string into class name
this
.
file_type_class
=
function
(
type
)
{
if
(
!
type
)
return
''
;
var
classes
=
[];
classes
.
push
(
type
.
replace
(
/\/.*/
,
''
));
classes
.
push
(
type
.
replace
(
/[^a-z0-9]/g
,
'_'
));
return
classes
.
join
(
' '
);
};
// convert bytes into number with size unit
this
.
file_size
=
function
(
size
)
{
if
(
size
>=
1073741824
)
return
parseFloat
(
size
/
1073741824
).
toFixed
(
2
)
+
' GB'
;
if
(
size
>=
1048576
)
return
parseFloat
(
size
/
1048576
).
toFixed
(
2
)
+
' MB'
;
if
(
size
>=
1024
)
return
parseInt
(
size
/
1024
)
+
' kB'
;
return
parseInt
(
size
||
0
)
+
' B'
;
};
// Extract file name from full path
this
.
file_name
=
function
(
path
)
{
var
path
=
path
.
split
(
this
.
env
.
directory_separator
);
return
path
.
pop
();
};
// Extract file path from full path
this
.
file_path
=
function
(
path
)
{
var
path
=
path
.
split
(
this
.
env
.
directory_separator
);
path
.
pop
();
return
path
.
join
(
this
.
env
.
directory_separator
);
};
// compare two sortable objects
this
.
sort_compare
=
function
(
data1
,
data2
)
{
var
key
=
this
.
env
.
sort_col
||
'name'
;
if
(
key
==
'mtime'
)
key
=
'modified'
;
data1
=
data1
[
key
];
data2
=
data2
[
key
];
if
(
key
==
'size'
||
key
==
'modified'
)
// numeric comparison
return
this
.
env
.
sort_reverse
?
data2
-
data1
:
data1
-
data2
;
else
{
// use Array.sort() for string comparison
var
arr
=
[
data1
,
data2
];
arr
.
sort
(
function
(
a
,
b
)
{
// @TODO: use localeCompare() arguments for better results
return
a
.
localeCompare
(
b
);
});
if
(
this
.
env
.
sort_reverse
)
arr
.
reverse
();
return
arr
[
0
]
===
data2
?
1
:
-
1
;
}
};
// Checks if specified mimetype is supported natively by the browser (return 1)
// or can be displayed in the browser using File API viewer (return 2)
// or is editable (using File API viewer, Manticore or WOPI) (return 4)
this
.
file_type_supported
=
function
(
type
,
capabilities
)
{
var
i
,
t
,
res
=
0
,
regexps
=
[],
img
=
'jpg|jpeg|gif|bmp|png'
,
caps
=
this
.
env
.
browser_capabilities
||
{},
doc
=
/^application\/vnd.oasis.opendocument.(text)$/i
;
type
=
String
(
type
).
toLowerCase
();
if
(
capabilities
)
{
// Manticore?
$
.
each
(
capabilities
.
MANTICORE_EDITABLE
||
[],
function
()
{
if
(
type
==
this
)
{
res
|=
4
;
return
false
;
}
});
// old version of the check
if
(
capabilities
&&
capabilities
.
MANTICORE
&&
doc
.
test
(
type
))
res
|=
4
;
// WOPI (Collabora Online)?
$
.
each
(
capabilities
.
WOPI_EDITABLE
||
[],
function
()
{
if
(
type
==
this
)
{
res
|=
4
;
return
false
;
}
});
}
if
(
caps
.
tiff
)
img
+=
'|tiff'
;
if
((
new
RegExp
(
'^image/('
+
img
+
')$'
,
'i'
)).
test
(
type
))
res
|=
1
;
// prefer text viewer for any text type
if
(
/^text\/(?!(pdf|x-pdf))/i
.
test
(
type
))
res
|=
2
|
4
;
if
(
caps
.
pdf
)
{
regexps
.
push
(
/^application\/(pdf|x-pdf|acrobat|vnd.pdf)/i
);
regexps
.
push
(
/^text\/(pdf|x-pdf)/i
);
}
if
(
caps
.
flash
)
regexps
.
push
(
/^application\/x-shockwave-flash/i
);
for
(
i
in
regexps
)
if
(
regexps
[
i
].
test
(
type
))
res
|=
1
;
for
(
i
in
navigator
.
mimeTypes
)
{
t
=
navigator
.
mimeTypes
[
i
].
type
;
if
(
t
==
type
&&
navigator
.
mimeTypes
[
i
].
enabledPlugin
)
res
|=
1
;
}
// types with viewer support
if
(
$
.
inArray
(
type
,
this
.
env
.
supported_mimetypes
)
>
-
1
)
res
|=
2
;
return
res
;
};
// Return browser capabilities
this
.
browser_capabilities
=
function
()
{
var
i
,
caps
=
[],
ctypes
=
[
'pdf'
,
'flash'
,
'tiff'
];
for
(
i
in
ctypes
)
if
(
this
.
env
.
browser_capabilities
[
ctypes
[
i
]])
caps
.
push
(
ctypes
[
i
]);
return
caps
;
};
// Checks browser capabilities eg. PDF support, TIF support
this
.
browser_capabilities_check
=
function
()
{
if
(
!
this
.
env
.
browser_capabilities
)
this
.
env
.
browser_capabilities
=
{};
if
(
this
.
env
.
browser_capabilities
.
pdf
===
undefined
)
this
.
env
.
browser_capabilities
.
pdf
=
this
.
pdf_support_check
();
if
(
this
.
env
.
browser_capabilities
.
flash
===
undefined
)
this
.
env
.
browser_capabilities
.
flash
=
this
.
flash_support_check
();
if
(
this
.
env
.
browser_capabilities
.
tiff
===
undefined
)
this
.
tiff_support_check
();
};
this
.
tiff_support_check
=
function
()
{
var
img
=
new
Image
(),
ref
=
this
;
img
.
onload
=
function
()
{
ref
.
env
.
browser_capabilities
.
tiff
=
1
;
};
img
.
onerror
=
function
()
{
ref
.
env
.
browser_capabilities
.
tiff
=
0
;
};
img
.
src
=
this
.
env
.
resources_dir
+
'/blank.tiff'
;
};
this
.
pdf_support_check
=
function
()
{
var
plugin
=
navigator
.
mimeTypes
?
navigator
.
mimeTypes
[
"application/pdf"
]
:
{},
plugins
=
navigator
.
plugins
,
len
=
plugins
.
length
,
regex
=
/Adobe Reader|PDF|Acrobat/i
,
ref
=
this
;
if
(
plugin
&&
plugin
.
enabledPlugin
)
return
1
;
if
(
window
.
ActiveXObject
)
{
try
{
if
(
axObj
=
new
ActiveXObject
(
"AcroPDF.PDF"
))
return
1
;
}
catch
(
e
)
{}
try
{
if
(
axObj
=
new
ActiveXObject
(
"PDF.PdfCtrl"
))
return
1
;
}
catch
(
e
)
{}
}
for
(
i
=
0
;
i
<
len
;
i
++
)
{
plugin
=
plugins
[
i
];
if
(
typeof
plugin
===
'String'
)
{
if
(
regex
.
test
(
plugin
))
return
1
;
}
else
if
(
plugin
.
name
&&
regex
.
test
(
plugin
.
name
))
return
1
;
}
return
0
;
};
this
.
flash_support_check
=
function
()
{
var
plugin
=
navigator
.
mimeTypes
?
navigator
.
mimeTypes
[
"application/x-shockwave-flash"
]
:
{};
if
(
plugin
&&
plugin
.
enabledPlugin
)
return
1
;
if
(
window
.
ActiveXObject
)
{
try
{
if
(
axObj
=
new
ActiveXObject
(
"ShockwaveFlash.ShockwaveFlash"
))
return
1
;
}
catch
(
e
)
{}
}
return
0
;
};
// converts number of seconds into HH:MM:SS format
this
.
time_format
=
function
(
s
)
{
s
=
parseInt
(
s
);
if
(
s
>=
60
*
60
*
24
)
return
'-'
;
return
(
new
Date
(
1970
,
1
,
1
,
0
,
0
,
s
,
0
)).
toTimeString
().
replace
(
/.*(\d{2}:\d{2}:\d{2}).*/
,
'$1'
);
};
// same as str.split(delimiter) but it ignores delimiters within quoted strings
this
.
explode_quoted_string
=
function
(
str
,
delimiter
)
{
var
result
=
[],
strlen
=
str
.
length
,
q
,
p
,
i
,
chr
,
last
;
for
(
q
=
p
=
i
=
0
;
i
<
strlen
;
i
++
)
{
chr
=
str
.
charAt
(
i
);
if
(
chr
==
'"'
&&
last
!=
'\\'
)
{
q
=
!
q
;
}
else
if
(
!
q
&&
chr
==
delimiter
)
{
result
.
push
(
str
.
substring
(
p
,
i
));
p
=
i
+
1
;
}
last
=
chr
;
}
result
.
push
(
str
.
substr
(
p
));
return
result
;
};
};
/**
* Class implementing Document Editor Host API
* supporting Manticore and Collabora Online
*
* Configuration:
* iframe - editor iframe element
* title_input - document title element
* export_menu - export formats list
* members_list - collaborators list
* photo_url - <img> src for a collaborator
* photo_default_url - default image of a collaborator
* domain - iframe origin
*
* set_busy, display_message, hide_message, gettext - common methods
*
* api - Chwala files_api instance
* interval - how often to check for invitations in seconds (default: 60)
* owner - user identifier
* invitationMore - add "more" link into invitation notices
* invitationChange - method to handle invitation state updates
* invitationSave - method to handle invitation state update
*/
function
document_editor_api
(
conf
)
{
var
domain
,
editor
,
is_wopi
=
false
,
locks
=
{},
callbacks
=
{},
members
=
{},
self
=
this
;
// Sets state
this
.
set_busy
=
function
(
state
,
message
)
{
if
(
conf
.
set_busy
)
return
conf
.
set_busy
(
state
,
message
);
};
// Displays error/notification message
this
.
display_message
=
function
(
label
,
type
,
is_txt
,
timeout
)
{
if
(
conf
.
display_message
)
return
conf
.
display_message
(
label
,
type
,
is_txt
,
timeout
);
if
(
type
==
'error'
)
alert
(
is_txt
?
label
:
this
.
gettext
(
label
));
};
// Hides the error/notification message
this
.
hide_message
=
function
(
id
)
{
if
(
conf
.
hide_message
)
return
conf
.
hide_message
(
id
);
};
// Localization method
this
.
gettext
=
function
(
label
)
{
if
(
conf
.
gettext
)
return
conf
.
gettext
(
label
);
return
label
;
};
// Handle messages from the editor
this
.
message_handler
=
function
(
data
)
{
var
result
;
data
=
this
.
message_convert
(
data
);
if
(
data
.
id
&&
callbacks
[
data
.
id
])
result
=
callbacks
[
data
.
id
](
data
);
else
if
(
typeof
data
.
callback
==
'function'
)
result
=
data
.
callback
(
data
);
if
(
result
!==
false
&&
data
.
name
&&
conf
[
data
.
name
])
result
=
conf
[
data
.
name
](
data
);
delete
callbacks
[
data
.
id
];
if
(
locks
[
data
.
id
])
{
this
.
set_busy
(
false
);
this
.
hide_message
(
data
.
id
);
delete
locks
[
data
.
id
];
}
if
(
result
===
false
)
return
;
switch
(
data
.
name
)
{
case
'ready'
:
this
.
ready
();
break
;
case
'titleChanged'
:
if
(
conf
.
title_input
)
$
(
conf
.
title_input
).
val
(
data
.
value
);
break
;
case
'memberAdded'
:
// @TODO: display notification?
if
(
conf
.
members_list
)
$
(
conf
.
members_list
).
append
(
this
.
member_item
(
data
));
break
;
case
'memberRemoved'
:
// @TODO: display notification?
if
(
conf
.
members_list
&&
members
[
data
.
memberId
])
{
$
(
'#'
+
members
[
data
.
memberId
].
id
,
conf
.
members_list
).
remove
();
delete
members
[
data
.
memberId
];
}
break
;
case
'sessionClosed'
:
this
.
display_message
(
'sessionterminated'
,
'error'
);
break
;
}
};
// Convert WOPI postMessage into internal (Manticore) format
this
.
message_convert
=
function
(
data
)
{
// In WOPI data is JSON-stringified
if
(
$
.
type
(
data
)
==
'string'
)
data
=
JSON
.
parse
(
data
);
// non-WOPI format
if
(
!
data
.
MessageId
)
return
data
;
var
value
=
data
.
Values
,
result
=
{
name
:
data
.
MessageId
,
wopi
:
true
,
id
:
data
.
MessageId
},
member_fn
=
function
(
value
)
{
var
color
=
value
.
Color
;
// make sure color is in css hex format
if
(
color
)
{
if
(
$
.
type
(
color
)
==
'string'
&&
color
.
charAt
(
0
)
!=
'#'
)
color
=
'#'
+
color
;
else
if
(
$
.
type
(
color
)
==
'number'
)
color
=
((
color
)
>>>
0
).
toString
(
16
).
slice
(
-
6
);
color
=
'#'
+
(
"000000"
).
substring
(
0
,
6
-
color
.
length
)
+
color
;
}
return
{
memberId
:
value
.
ViewId
,
email
:
value
.
UserId
,
fullName
:
value
.
UserName
,
color
:
color
};
};
switch
(
result
.
name
)
{
// WOPI editor is ready
case
'App_LoadingStatus'
:
is_wopi
=
true
;
result
.
name
=
'ready'
;
// Enable Save button, there's no documentChanged event in WOPI
if
(
typeof
conf
.
documentChanged
==
'function'
)
conf
.
documentChanged
();
break
;
// WOPI session member exited
case
'View_Removed'
:
result
.
name
=
'memberRemoved'
;
result
.
memberId
=
value
.
ViewId
;
break
;
// WOPI session member entered
case
'View_Added'
:
result
.
name
=
'memberAdded'
;
$
.
extend
(
result
,
member_fn
(
value
));
break
;
// Listing WOPI session members
case
'Get_Views_Resp'
:
result
.
list
=
$
.
map
(
value
||
[],
member_fn
);
result
.
callback
=
function
(
data
)
{
self
.
members_list
(
data
.
list
);
return
false
;
};
break
;
case
'Session_Closed'
:
result
.
name
=
'sessionClosed'
;
break
;
case
'Get_Export_Formats_Resp'
:
result
.
list
=
$
.
map
(
value
||
[],
function
(
v
)
{
return
{
label
:
v
.
Label
,
format
:
v
.
Format
};
});
result
.
callback
=
function
(
data
)
{
self
.
export_menu_init
(
data
.
list
);
return
false
;
};
break
;
}
return
result
;
};
// Sends Manticore postMessage
this
.
post
=
function
(
action
,
data
,
callback
,
lock_label
)
{
if
(
is_wopi
)
{
// replace Manticore messages with WOPI messages
// ignore unsupported functionality
switch
(
action
)
{
case
'getMembers'
:
// ignore, Collabora Online sends View_Added for current user
break
;
case
'actionSave'
:
this
.
wopi_post
(
'Action_Save'
,
{
DontTerminateEdit
:
true
,
DontSaveIfUnmodified
:
true
});
// Enable Save button, there's no documentChanged event in WOPI
if
(
typeof
conf
.
documentChanged
==
'function'
)
conf
.
documentChanged
();
break
;
case
'ActionExport'
:
this
.
wopi_post
(
'Action_Export'
,
{
Format
:
data
.
value
});
break
;
case
'getExportFormats'
:
this
.
wopi_post
(
'Get_Export_Formats'
);
break
;
}
return
;
}
if
(
!
data
)
data
=
{};
if
(
lock_label
)
{
data
.
id
=
this
.
set_busy
(
true
,
this
.
gettext
(
lock_label
));
locks
[
data
.
id
]
=
true
;
}
if
(
!
data
.
id
)
data
.
id
=
(
new
Date
).
getTime
();
// make sure the id is not in use
while
(
callbacks
[
data
.
id
])
data
.
id
++
;
data
.
name
=
action
;
callbacks
[
data
.
id
]
=
callback
;
editor
.
postMessage
(
data
,
domain
);
};
// Sends WOPI postMessage
this
.
wopi_post
=
function
(
action
,
data
)
{
var
msg
=
{
MessageId
:
action
,
SendTime
:
Date
.
now
(),
Values
:
data
||
{}
};
editor
.
postMessage
(
JSON
.
stringify
(
msg
),
domain
);
};
// Callback for 'ready' message
this
.
ready
=
function
()
{
if
(
this
.
init_lock
)
{
this
.
set_busy
(
false
);
this
.
hide_message
(
this
.
init_lock
);
delete
this
.
init_lock
;
}
if
(
conf
.
export_menu
)
this
.
export_menu
(
conf
.
export_menu
);
if
(
conf
.
members_list
)
this
.
get_members
(
function
(
data
)
{
self
.
members_list
(
data
.
value
);
});
if
(
conf
.
title_input
)
this
.
get_title
(
function
(
data
)
{
$
(
conf
.
title_input
).
val
(
data
.
value
);
});
};
// Save current document
this
.
save
=
function
(
callback
)
{
this
.
post
(
'actionSave'
,
{},
callback
,
'saving'
);
};
// Terminate session
this
.
terminate
=
function
()
{
// we send it only to WOPI editor, Manticore session will
// be terminated by Chwala backend
if
(
is_wopi
)
this
.
wopi_post
(
'Close_Session'
);
};
// Print document
this
.
print
=
function
()
{
// this is implemented only by WOPI editor
if
(
is_wopi
)
this
.
wopi_post
(
'Action_Print'
);
};
// Export/download current document
this
.
export
=
function
(
type
,
callback
)
{
this
.
post
(
'actionExport'
,
{
value
:
type
},
callback
);
};
// Get supported export formats and create content of menu element
this
.
export_menu
=
function
(
menu
)
{
this
.
post
(
'getExportFormats'
,
{},
function
(
data
)
{
self
.
export_menu_init
(
data
.
value
);
});
};
this
.
export_formats_init
=
function
(
formats
)
{
var
items
=
[];
$
.
each
(
formats
||
[],
function
(
i
,
v
)
{
items
.
push
(
$
(
'<li>'
).
attr
({
role
:
'menuitem'
}).
append
(
$
(
'<a>'
).
attr
({
href
:
'#'
,
role
:
'button'
,
tabindex
:
0
,
'aria-disabled'
:
false
,
'class'
:
'active'
})
.
text
(
v
.
label
).
click
(
function
()
{
self
.
export
(
v
.
format
);
})
));
});
$
(
menu
).
html
(
''
).
append
(
items
);
};
// Get document title
this
.
get_title
=
function
(
callback
)
{
this
.
post
(
'getTitle'
,
{},
callback
);
};
// Set document title
this
.
set_title
=
function
(
title
,
callback
)
{
this
.
post
(
'setTitle'
,
{
value
:
title
},
callback
);
};
// Get document session members
this
.
get_members
=
function
(
callback
)
{
this
.
post
(
'getMembers'
,
{},
callback
);
};
// Creates session member image element
this
.
member_item
=
function
(
member
,
id
)
{
member
.
id
=
'member'
+
(
id
||
(
new
Date
).
getTime
());
member
.
name
=
member
.
fullName
+
' ('
+
member
.
email
+
')'
;
members
[
member
.
memberId
]
=
member
;
var
img
=
$
(
'<img>'
).
attr
({
title
:
member
.
name
,
id
:
member
.
id
,
'class'
:
'photo'
,
src
:
conf
.
photo_default_url
})
.
css
({
'border-color'
:
member
.
color
})
.
text
(
name
);
if
(
conf
.
photo_url
)
{
img
.
attr
(
'src'
,
conf
.
photo_url
.
replace
(
/%email/
,
urlencode
(
member
.
email
)));
if
(
conf
.
photo_default_url
)
img
.
on
(
'error'
,
function
()
{
this
.
src
=
conf
.
photo_default_url
;
});
}
return
img
;
};
this
.
members_list
=
function
(
list
)
{
var
images
=
[],
id
=
(
new
Date
).
getTime
();
$
.
each
(
list
||
[],
function
()
{
images
.
push
(
self
.
member_item
(
this
,
id
++
));
});
$
(
conf
.
members_list
).
html
(
''
).
append
(
images
);
};
// track changes in invitations
this
.
track_invitations
=
function
()
{
conf
.
api
.
request
(
'invitations'
,
{
timestamp
:
this
.
invitations_timestamp
||
-
1
},
this
.
parse_invitations
);
this
.
invitations_timeout
=
setTimeout
(
function
()
{
self
.
track_invitations
();
},
(
conf
.
interval
||
60
)
*
1000
);
};
// parse 'invitations' response
this
.
parse_invitations
=
function
(
response
)
{
if
(
!
conf
.
api
.
response
(
response
)
||
!
response
.
result
)
return
;
var
invitation_change
=
function
(
invitation
)
{
var
msg
=
self
.
invitation_msg
(
invitation
);
if
(
conf
.
invitationMore
)
msg
=
$
(
'<div>'
)
.
append
(
$
(
'<span>'
).
text
(
msg
+
' '
))
.
append
(
$
(
'<a>'
).
text
(
self
.
gettext
(
'more'
)).
attr
(
'id'
,
invitation
.
id
)).
html
();
self
.
display_message
(
msg
,
'notice'
,
true
,
30
);
// update existing sessions info
if
(
conf
.
api
&&
conf
.
api
.
sessions
&&
invitation
.
file
)
{
var
session
,
folder
=
conf
.
api
.
file_path
(
invitation
.
file
),
is_invited
=
function
()
{
return
!
invitation
.
is_session_owner
&&
/^(invited|accepted)/
.
test
(
invitation
.
status
);
};
$
.
each
(
conf
.
api
.
sessions
[
folder
]
||
{},
function
(
i
,
s
)
{
if
(
i
==
invitation
.
session_id
||
s
.
file
==
invitation
.
file
)
{
if
(
is_invited
())
conf
.
api
.
sessions
[
folder
][
i
].
is_invited
=
true
;
if
(
s
.
id
==
invitation
.
session_id
)
session
=
conf
.
api
.
sessions
[
folder
][
i
];
}
});
if
(
!
session
)
{
if
(
!
conf
.
api
.
sessions
[
folder
])
conf
.
api
.
sessions
[
folder
]
=
{};
conf
.
api
.
sessions
[
folder
][
invitation
.
session_id
]
=
{
owner
:
invitation
.
owner
,
owner_name
:
invitation
.
owner_name
,
is_owner
:
invitation
.
is_session_owner
,
is_invited
:
is_invited
(),
file
:
invitation
.
file
};
}
}
if
(
conf
.
invitationChange
)
conf
.
invitationChange
(
invitation
);
}
$
.
each
(
response
.
result
.
list
||
[],
function
(
i
,
invitation
)
{
invitation
.
id
=
'i'
+
(
response
.
result
.
timestamp
+
i
);
invitation
.
is_session_owner
=
invitation
.
user
!=
conf
.
owner
;
// display notifications
if
(
!
invitation
.
is_session_owner
)
{
if
(
invitation
.
status
==
'invited'
||
invitation
.
status
==
'declined-owner'
||
invitation
.
status
==
'accepted-owner'
)
{
invitation_change
(
invitation
);
}
}
else
{
if
(
invitation
.
status
==
'accepted'
||
invitation
.
status
==
'declined'
||
invitation
.
status
==
'requested'
)
{
invitation_change
(
invitation
);
}
}
});
self
.
invitations_timestamp
=
response
.
result
.
timestamp
;
};
this
.
invitation_msg
=
function
(
invitation
)
{
return
self
.
gettext
(
invitation
.
status
.
replace
(
'-'
,
''
)
+
'notice'
)
.
replace
(
'$user'
,
invitation
.
user_name
?
invitation
.
user_name
:
invitation
.
user
)
.
replace
(
'$owner'
,
invitation
.
owner_name
?
invitation
.
owner_name
:
invitation
.
owner
)
.
replace
(
'$file'
,
invitation
.
filename
);
};
// Request access to the editing session
this
.
invitation_request
=
function
(
invitation
)
{
var
params
=
{
id
:
invitation
.
session_id
,
user
:
invitation
.
user
||
''
};
conf
.
api
.
req
=
this
.
set_busy
(
true
,
'invitationrequesting'
);
conf
.
api
.
request
(
'document_request'
,
params
,
function
(
response
)
{
self
.
invitation_response
(
response
,
invitation
,
'requested'
);
});
};
// Accept an invitations to the editing session
this
.
invitation_accept
=
function
(
invitation
)
{
var
params
=
{
id
:
invitation
.
session_id
,
user
:
invitation
.
user
||
''
};
conf
.
api
.
req
=
this
.
set_busy
(
true
,
'invitationaccepting'
);
conf
.
api
.
request
(
'document_accept'
,
params
,
function
(
response
)
{
self
.
invitation_response
(
response
,
invitation
,
'accepted'
);
});
};
// Decline an invitations to the editing session
this
.
invitation_decline
=
function
(
invitation
)
{
var
params
=
{
id
:
invitation
.
session_id
,
user
:
invitation
.
user
||
''
};
conf
.
api
.
req
=
this
.
set_busy
(
true
,
'invitationdeclining'
);
conf
.
api
.
request
(
'document_decline'
,
params
,
function
(
response
)
{
self
.
invitation_response
(
response
,
invitation
,
'declined'
);
});
};
// document_decline response handler
this
.
invitation_response
=
function
(
response
,
invitation
,
status
)
{
if
(
!
conf
.
api
.
response
(
response
))
return
;
invitation
.
status
=
status
;
if
(
conf
.
invitationSaved
)
conf
.
invitationSaved
(
invitation
);
};
if
(
!
conf
)
conf
=
{};
// Got editor iframe, use editor's API
if
(
conf
.
iframe
)
{
editor
=
conf
.
iframe
.
contentWindow
;
domain
=
conf
.
domain
;
if
(
!
domain
&&
/^(https?:\/\/[^/]+)/i
.
test
(
conf
.
iframe
.
src
))
domain
=
RegExp
.
$1
;
// Register 'message' event to receive messages from the editor iframe
window
.
addEventListener
(
'message'
,
function
(
event
)
{
if
(
event
.
source
==
editor
&&
event
.
origin
==
domain
)
{
self
.
message_handler
(
event
.
data
);
}
});
// Bind for document title changes
if
(
conf
.
title_input
)
$
(
conf
.
title_input
).
change
(
function
()
{
self
.
set_title
(
$
(
this
).
val
());
});
// Display loading message
this
.
init_lock
=
this
.
set_busy
(
true
,
'loading'
);
this
.
wopi_post
(
'Host_PostmessageReady'
);
}
if
(
conf
.
api
)
this
.
track_invitations
();
};
// Add escape() method to RegExp object
// http://dev.rubyonrails.org/changeset/7271
RegExp
.
escape
=
function
(
str
)
{
return
String
(
str
).
replace
(
/([.*+?^=!:${}()|[\]\/\\])/g
,
'\\$1'
);
};
// define String's startsWith() method for old browsers
if
(
!
String
.
prototype
.
startsWith
)
{
String
.
prototype
.
startsWith
=
function
(
search
,
position
)
{
position
=
position
||
0
;
return
this
.
slice
(
position
,
search
.
length
)
===
search
;
};
};
// make a string URL safe (and compatible with PHP's rawurlencode())
function
urlencode
(
str
)
{
if
(
window
.
encodeURIComponent
)
return
encodeURIComponent
(
str
).
replace
(
'*'
,
'%2A'
);
return
escape
(
str
)
.
replace
(
'+'
,
'%2B'
)
.
replace
(
'*'
,
'%2A'
)
.
replace
(
'/'
,
'%2F'
)
.
replace
(
'@'
,
'%40'
);
};
function
escapeHTML
(
str
)
{
return
str
===
undefined
?
''
:
String
(
str
)
.
replace
(
/&/g
,
'&'
)
.
replace
(
/>/g
,
'>'
)
.
replace
(
/</g
,
'<'
);
};
function
object_is_empty
(
obj
)
{
if
(
obj
)
for
(
var
i
in
obj
)
if
(
i
!==
null
)
return
true
;
return
false
;
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jun 9, 7:41 AM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196778
Default Alt Text
files_api.js (33 KB)
Attached To
Mode
R26 chwala
Attached
Detach File
Event Timeline
Log In to Comment