diff --git a/data/core.telegram.org/api/bots/commands.html b/data/core.telegram.org/api/bots/commands.html new file mode 100644 index 0000000000..4a2dc42d6c --- /dev/null +++ b/data/core.telegram.org/api/bots/commands.html @@ -0,0 +1,140 @@ + + + + + Commands + + + + + + + + + + + + + +
+ +
+
+
+ +

Commands

+ +
+ +

Bots offer a set of commands that can be used by users in private, or in a chat.

+

For a simplified description using the HTTP bot API, see here ».

+

Getting commands

+
botCommand#c27ac8c7 command:string description:string = BotCommand;
+
+botInfo#1b74b335 user_id:long description:string commands:Vector<BotCommand> = BotInfo;
+
+channelFull#e9b27a17 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string = ChatFull;
+userFull#d697ff05 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string = UserFull;
+
+user#3ff6ecb0 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
+

The botInfo constructors contained in the userFull, chatFull, channelFull contain a list of commands, and for groups, the ID and a description of each bot.

+

In graphical clients, when users begin a message with a /, a list of commands supported by all bots present in the current chat should be shown; the same should be done for one-to-one chats with the bot itself.

+

If the command list of a bot changes, the bot_info_version contained in the user constructor received in updates will change; this indicates that the client should refetch full bot information using users.getFullUser.

+

Setting commands

+
botCommand#c27ac8c7 command:string description:string = BotCommand;
+
+---functions---
+
+bots.setBotCommands#517165a scope:BotCommandScope lang_code:string commands:Vector<BotCommand> = Bool;
+

The command list can be changed by the owner of the bot through @botfather, but bots can also change their own command list by invoking bots.setBotCommands.

+ +
+ +
+
+ +
+ + + + + + + + diff --git a/data/core.telegram.org/api/obtaining_api_id.html b/data/core.telegram.org/api/obtaining_api_id.html new file mode 100644 index 0000000000..e91ef8a48b --- /dev/null +++ b/data/core.telegram.org/api/obtaining_api_id.html @@ -0,0 +1,140 @@ + + + + + Creating your Telegram Application + + + + + + + + + + + + + +
+ +
+
+
+ +

Creating your Telegram Application

+ +

We welcome all developers to use our API and source code to create Telegram-like messaging applications on our platform free of charge.

+
+

In order to ensure consistency and security across the Telegram ecosystem, +all third-party client apps must comply with the API Terms of Service.

+
+

Obtaining api_id

+

In order to obtain an API id and develop your own application using the Telegram API you need to do the following:

+
    +
  • Sign up for Telegram using any application.
  • +
  • Log in to your Telegram core: https://my.telegram.org.
  • +
  • Go to 'API development tools' and fill out the form.
  • +
  • You will get basic addresses as well as the api_id and api_hash parameters required for user authorization.
  • +
  • For the moment each number can only have one api_id connected to it.
  • +
+

We will be sending important developer notifications to the phone number that you use in this process, so please use an up-to-date number connected to your active Telegram account.

+

Using the api_id

+

Before using the MTProto Telegram API, please note that all API client libraries are strictly monitored to prevent abuse.

+

If you use the Telegram API for flooding, spamming, faking subscriber and view counters of channels, you will be banned forever.

+

Due to excessive abuse of the Telegram API, all accounts that sign up or log in using unofficial Telegram API clients are automatically put under observation to avoid violations of the Terms of Service.

+

If you didn't violate the Terms of Service but your account does get banned after using the API, write to recover@telegram.org explaining what you intend to do with the API, asking to unban your account.
+Please note that emails are checked by a human, so automatically generated emails will be detected and banned.

+

Using Telegram's open source code

+

Everyone is welcome to use our open source code. We have included a sample API id with the code. This API id is limited on the server side and is not suitable for apps released to end-users — using it for anything but testing purposes will result in the API_ID_PUBLISHED_FLOOD error for your users. It is necessary that you obtain your own API id before you publish your app.

+
+

Please remember to publish your code as well in order to comply with the GNU GPL licences.

+
+ +
+ +
+
+ +
+ + + + + + diff --git a/data/core.telegram.org/api/terms.html b/data/core.telegram.org/api/terms.html new file mode 100644 index 0000000000..20985c7cf7 --- /dev/null +++ b/data/core.telegram.org/api/terms.html @@ -0,0 +1,133 @@ + + + + + Telegram API Terms of Service + + + + + + + + + + + + + +
+ +
+
+
+ +

Telegram API Terms of Service

+ +
+ +

We welcome all developers to use our API and source code to create Telegram-like messaging applications on our platform free of charge. In order to ensure consistency and security across the Telegram ecosystem, all third-party client apps must comply with the following Terms of Service.

+

1. Privacy & Security

+

1.1. Telegram is a privacy-oriented platform. All client apps must, therefore, guard their users' privacy with utmost care and comply with our Security Guidelines.
1.2. Developers are welcome to add new features or improve and extend existing Telegram features provided that these modifications do not violate these Terms of Service.
1.3. As a client developer, you must make sure that all the basic features of the main Telegram apps function correctly and in an expected way both in your app and when users of your app communicate with other Telegram users. It is forbidden to force users of other Telegram clients to download your app in order to view certain messages and content sent using your app.
1.4. It is forbidden to interfere with the basic functionality of Telegram. This includes but is not limited to: making actions on behalf of the user without the user's knowledge and consent, preventing self-destructing content from disappearing, preventing last seen and online statuses from being displayed correctly, tampering with the 'read' statuses of messages (e.g. implementing a 'ghost mode'), preventing typing statuses from being sent/displayed, etc.

+

2. Transparency

+

2.1. You must obtain your own api_id for your application.
2.2. We offer our API free of charge, but your users must be aware of the fact that your app uses the Telegram API and is part of the Telegram ecosystem. This fact must be featured prominently in the app's description in the app stores and in the in-app intro if your app has it.
2.3. To avoid confusion, the title of your app must not include the word “Telegram”. An exception can be made if the word “Telegram” is preceded with the word “Unofficial” in the title.
2.4. You must not use the official Telegram logo for your app. Both the Telegram brand and its logo are registered trademarks protected by law in almost every country.

+

3. Advertising & Monetization

+

3.1. Developers are allowed to monetize their coding efforts through advertising or other legitimate means.
3.2. If you decide to monetize your app, you must clearly mention all the methods of monetization that are used in your app in all its app store descriptions.
3.3. If your app allows accessing content from Telegram channels, you must include support for official sponsored messages in Telegram channels and may not interefere with this functionality.

+

4. Breach of terms

+

4.1. If your app violates these terms, we will notify the Telegram account responsible for the app about the breach of terms.
4.2. If you do not update the app to fix the highlighted issues within 10 days, we will have to discontinue your access to Telegram API and contact the app stores about the removal of your apps that are using the Telegram API in violation of these terms.

+

We reserve the right to expand these terms and guidelines as the need arises. We will inform client developers of such changes via an in-app notification to their accounts connected to the app in question.

+
+

Back to Creating Your Telegram Application »

+
+
+ +
+ +
+
+ +
+ + + + + + + + diff --git a/data/core.telegram.org/constructor/channel.html b/data/core.telegram.org/constructor/channel.html new file mode 100644 index 0000000000..7c95d40db2 --- /dev/null +++ b/data/core.telegram.org/constructor/channel.html @@ -0,0 +1,289 @@ + + + + + channel + + + + + + + + + + + + + +
+ +
+
+
+ +

channel

+ +

Channel/supergroup info

+

+ +
+
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;

+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
flags#Flags, see TL conditional fields
creatorflags.0?trueWhether the current user is the creator of this channel
leftflags.2?trueWhether the current user has left this channel
broadcastflags.5?trueIs this a channel?
verifiedflags.7?trueIs this channel verified by telegram?
megagroupflags.8?trueIs this a supergroup?
restrictedflags.9?trueWhether viewing/writing in this channel for a reason (see restriction_reason
signaturesflags.11?trueWhether signatures are enabled (channels)
minflags.12?trueSee min
scamflags.19?trueThis channel/supergroup is probably a scam
has_linkflags.20?trueWhether this channel has a private join link
has_geoflags.21?trueWhether this chanel has a geoposition
slowmode_enabledflags.22?trueWhether slow mode is enabled for groups to prevent flood in chat
call_activeflags.23?trueWhether a group call or livestream is currently active
call_not_emptyflags.24?trueWhether there's anyone in the group call or livestream
fakeflags.25?trueIf set, this supergroup/channel was reported by many users as a fake or scam: be careful when interacting with it.
gigagroupflags.26?trueWhether this supergroup is a gigagroup
idlongID of the channel
access_hashflags.13?longAccess hash
titlestringTitle
usernameflags.6?stringUsername
photoChatPhotoProfile photo
dateintDate when the user joined the supergroup/channel, or if the user isn't a member, its creation date
restriction_reasonflags.9?Vector<RestrictionReason>Contains the reason why access to this channel must be restricted.
admin_rightsflags.14?ChatAdminRightsAdmin rights of the user in this channel (see rights)
banned_rightsflags.15?ChatBannedRightsBanned rights of the user in this channel (see rights)
default_banned_rightsflags.18?ChatBannedRightsDefault chat rights (see rights)
participants_countflags.17?intParticipant count
+

Type

+

Chat

+

Related pages

+

Min constructors

+

In some situations user and channel constructors have reduced set of fields present (although id is always there) and min flag set.

+

Channels

+

How to handle channels, supergroups, groups, and what's the difference between them.

+

Admin, banned, default rights

+

How to handle admin permissions, granular bans and global permissions in channels, groups and supergroups.

+ +
+ +
+
+ +
+ + + + + + diff --git a/data/core.telegram.org/constructor/updateChatUserTyping.html b/data/core.telegram.org/constructor/updateChatUserTyping.html new file mode 100644 index 0000000000..bbf511b070 --- /dev/null +++ b/data/core.telegram.org/constructor/updateChatUserTyping.html @@ -0,0 +1,160 @@ + + + + + updateChatUserTyping + + + + + + + + + + + + + +
+ +
+
+
+ +

updateChatUserTyping

+ +

The user is preparing a message in a group; typing, recording, uploading, etc. This update is valid for 6 seconds. If no repeated update received after 6 seconds, it should be considered that the user stopped doing whatever he's been doing.

+

+ +
+
updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction = Update;

+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
chat_idlongGroup id
from_idPeerPeer that started typing (can be the chat itself, in case of anonymous admins).
actionSendMessageActionType of action
Parameter added in Layer 17.
+

Type

+

Update

+

Related pages

+

Layers

+

Below you will find information on schema changes. For more details on the use of layers, see Invoking API methods.

+ +
+ +
+
+ +
+ + + + + + diff --git a/data/core.telegram.org/method/phone.confirmCall b/data/core.telegram.org/method/phone.confirmCall new file mode 100644 index 0000000000..0b2d55ce63 --- /dev/null +++ b/data/core.telegram.org/method/phone.confirmCall @@ -0,0 +1,188 @@ + + + + + phone.confirmCall + + + + + + + + + + + + + +
+ +
+
+
+ +

phone.confirmCall

+ +

Complete phone call E2E encryption key exchange »

+

+ +
+
phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector<User> = phone.PhoneCall;
+---functions---
+phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;

+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
peerInputPhoneCallThe phone call
g_abytesParameter for E2E encryption key exchange »
key_fingerprintlongKey fingerprint
protocolPhoneCallProtocolPhone call settings
+

Result

+

phone.PhoneCall

+

Possible errors

+ + + + + + + + + + + + + + + + + + + + +
CodeTypeDescription
400CALL_ALREADY_DECLINEDThe call was already declined.
400CALL_PEER_INVALIDThe provided call peer object is invalid.
+

Related pages

+

End-to-End Encrypted Voice Calls

+ +
+ +
+
+ +
+ + + + + + diff --git a/data/corefork.telegram.org/api/pin.html b/data/corefork.telegram.org/api/pin.html deleted file mode 100644 index 501034af11..0000000000 --- a/data/corefork.telegram.org/api/pin.html +++ /dev/null @@ -1,149 +0,0 @@ - - - - - Pinned messages - - - - - - - - - - - - - -
- -
-
-
- -

Pinned messages

- -
- -

Telegram allows pinning multiple messages on top of a specific chat.

-
message#85d6cbe2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
-
-updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vector<int> pts:int pts_count:int = Update;
-updatePinnedChannelMessages#5bb98608 flags:# pinned:flags.0?true channel_id:long messages:Vector<int> pts:int pts_count:int = Update;
-
----functions---
-
-messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates;
-messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
-
-messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
-channels.getMessages#ad8c9a23 channel:InputChannel id:Vector<InputMessage> = messages.Messages;
-

The messages.updatePinnedMessage method can be used to pin or unpin a specific message in an arbitrary chat.
-The unpin flags specifies whether to unpin or pin the message, and pm_oneside specifies whether the message should only be pinned on the local side of a one-to-one chat.

-

messages.unpinAllMessages can be used to unpin all messages in a chat.

-

When (un)pinning messages, a updatePinnedMessages or updatePinnedChannelMessages update will be emitted, containing IDs of pinned or unpinned messages.

-

Pinned messages will also have the will also have the pinned flag of message set.

-

Getting pinned messages

-
userFull#d697ff05 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string = UserFull;
-chatFull#4dbdc099 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string = ChatFull;
-channelFull#e9b27a17 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string = ChatFull;
-
-inputMessagesFilterPinned#1bb00451 = MessagesFilter;
-
----functions---
-
-messages.search#a0fda762 flags:# peer:InputPeer q:string from_id:flags.0?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
-

The pinned_msg_id of userFull, chatFull, channelFull contains the ID of only the latest pinned message.
-To obtain a full list, use messages.search with inputMessagesFilterPinned filter.

- -
- -
-
- -
- - - - - - - - diff --git a/data/corefork.telegram.org/method/account.setPrivacy b/data/corefork.telegram.org/method/account.setPrivacy new file mode 100644 index 0000000000..0571d85d6d --- /dev/null +++ b/data/corefork.telegram.org/method/account.setPrivacy @@ -0,0 +1,181 @@ + + + + + account.setPrivacy + + + + + + + + + + + + + +
+ +
+
+
+ +

account.setPrivacy

+ +

Change privacy settings of current account

+

+ +
+
account.privacyRules#50a04e45 rules:Vector<PrivacyRule> chats:Vector<Chat> users:Vector<User> = account.PrivacyRules;
+---functions---
+account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> = account.PrivacyRules;

+

Parameters

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
keyInputPrivacyKeyPeers to which the privacy rules apply
rulesVector<InputPrivacyRule>New privacy rules
+

Result

+

account.PrivacyRules

+

Possible errors

+ + + + + + + + + + + + + + + + + + + + + + + + + +
CodeTypeDescription
400PRIVACY_KEY_INVALIDThe privacy key is invalid.
400PRIVACY_TOO_LONGToo many privacy rules were specified, the current limit is 1000.
400PRIVACY_VALUE_INVALIDThe specified privacy rule combination is invalid.
+ +
+ +
+
+ +
+ + + + + + diff --git a/data/promote.telegram.org/js/jquery-ex.js b/data/promote.telegram.org/js/jquery-ex.js deleted file mode 100644 index 99da06fcf5..0000000000 --- a/data/promote.telegram.org/js/jquery-ex.js +++ /dev/null @@ -1,1683 +0,0 @@ -(function($) { - $.fn.redraw = function() { - return this.map(function(){ this.offsetTop; return this; }); - }; - $.fn.prepareSlideX = function(callback) { - return this.map(function(){ - $(this).css({width: this.scrollWidth, overflow: 'hidden'}); - return this; - }).one('transitionend', function(){ - $(this).css({width: '', overflow: ''}); - callback && callback.call(this); - }).redraw(); - }; - $.fn.prepareSlideY = function(callback) { - return this.map(function(){ - $(this).css({height: this.scrollHeight, overflow: 'hidden'}); - return this; - }).one('transitionend', function(){ - $(this).css({height: '', overflow: ''}); - callback && callback.call(this); - }).redraw(); - }; - $.fn.animOff = function(this_el) { - if (this_el) { - return this.css('transition', 'none').redraw(); - } - return this.addClass('no-transition').redraw(); - }; - $.fn.animOn = function(this_el) { - if (this_el) { - return this.redraw().css('transition', ''); - } - return this.redraw().removeClass('no-transition'); - }; - $.fn.fadeShow = function(callback) { - return this.fadeToggle(true, callback); - }; - $.fn.fadeHide = function(callback) { - return this.fadeToggle(false, callback); - }; - $.fn.isFadeHidden = function() { - return this.hasClass('ohide'); - }; - $.fn.isFixed = function() { - return this.parents().map(function(){ return $(this).css('position'); }).get().indexOf('fixed') != -1; - }; - $.fn.focusAndSelect = function(select_all) { - var field = this.get(0), len = this.value().length; - if (field) { - field.focus(); - if (len > 0) { - if (this.is('[contenteditable]')) { - var range = document.createRange(), sel; - range.selectNodeContents(field); - if (!select_all) { - range.collapse(); - } - sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - } else if (field.setSelectionRange) { - if (select_all) { - field.setSelectionRange(0, len); - } else { - field.setSelectionRange(len, len); - } - } - } - } - return this; - }; - $.fn.focusAndSelectAll = function() { - return this.focusAndSelect(true); - }; - $.fn.fadeToggle = function(state, callback) { - if (state === true || state === false) { - state = !state; - } - if (callback == 'remove') { - callback = function(){ $(this).remove(); }; - } - if (callback) { - this.one('transitionend', callback); - } - return this.toggleClass('ohide', state); - }; - $.fn.slideShow = function(callback) { - return this.prepareSlideY(callback).removeClass('shide'); - }; - $.fn.slideHide = function(callback) { - if (callback == 'remove') { - callback = function(){ $(this).remove(); }; - } - return this.prepareSlideY(callback).addClass('shide'); - }; - $.fn.slideXShow = function(callback) { - return this.prepareSlideX(callback).removeClass('sxhide'); - }; - $.fn.slideXHide = function(callback) { - if (callback == 'remove') { - callback = function(){ $(this).remove(); }; - } - return this.prepareSlideX(callback).addClass('sxhide'); - }; - $.fn.isSlideHidden = function() { - return this.hasClass('shide'); - }; - $.fn.slideToggle = function(state, callback) { - if (state === true || state === false) { - state = !state; - } - return this.prepareSlideY(callback).toggleClass('shide', state); - }; - $.fn.highlight = function(delay) { - var $this = this; - $this.addClass('highlight'); - setTimeout(function() { $this.removeClass('highlight'); }, delay); - return $this; - }; - $.fn.scrollIntoView = function(options) { - options = options || {}; - return this.first().each(function() { - var position = options.position || 'auto', - padding = options.padding || 0, - duration = options.duration || 0; - var $item = $(this), - $cont = $item.scrollParent(), - scrollTop = $cont.scrollTop(), - positionTop = 0, - paddingTop = 0, - itemHeight = $item.outerHeight(), - isBody = false; - if ($cont.get(0) === document) { - isBody = true; - $cont = $(window); - positionTop = $item.offset().top; - paddingTop = $('header').height() + 1; - } else { - positionTop = $item.offset().top - $cont.offset().top + scrollTop; - } - if (options.slidedEl) { - if (options.slidedEl === 'this') { - options.slidedEl = this; - } - $(options.slidedEl, this).each(function() { - itemHeight += (this.scrollHeight - this.clientHeight); - }); - } - var itemTop = positionTop, - itemBottom = itemTop + itemHeight, - contHeight = $cont.height(), - contTop = scrollTop + padding + paddingTop, - contBottom = scrollTop + contHeight - padding, - scrollTo = null; - if (position == 'auto') { - if (itemTop < contTop) { - scrollTo = itemTop - padding - paddingTop; - } else if (itemBottom > contBottom) { - if (itemHeight > contHeight - padding - padding) { - scrollTo = itemTop - padding - paddingTop; - } else { - scrollTo = itemBottom - contHeight + padding; - } - } - } else if (position == 'top' || position == 'center') { - if (position == 'center' && - contHeight > itemHeight) { - padding = (contHeight - paddingTop - itemHeight) / 2; - } - scrollTo = itemTop - padding - paddingTop; - } else if (position == 'bottom') { - if (itemHeight > contHeight - padding - padding) { - scrollTo = itemTop - padding - paddingTop; - } else { - scrollTo = itemBottom - contHeight + padding; - } - } - if (scrollTo) { - if (duration) { - if (isBody) { - $cont = $('html'); - } - $cont.stop().animate({scrollTop: scrollTo}, duration); - } else { - $cont.scrollTop(scrollTo); - } - } - }); - }; - $.fn.initSearch = function(options) { - return this.map(function(){ - var $field = $(this); - var curValue = $field.value(); - var curSelectedIndex = false; - var curResult = []; - var curRenderedIndex = 0; - var dataWaiting = false; - var keyUpTimeout = null; - var blurTimeout = null; - var isFocused = false; - options = options || {}; - if (!options.searchEnabled) { - options.searchEnabled = function(){ return true; }; - } - if (!options.enterEnabled) { - options.enterEnabled = function(){ return true; }; - } - if (!options.prepareQuery) { - options.prepareQuery = function(str){ return str.toLowerCase(); }; - } - $field.data('searchOptions', options); - - function onKeyDown(e) { - switch (e.which) { - case Keys.ESC: - $field.blur(); - break; - case Keys.RETURN: - select(curSelectedIndex); - break; - case Keys.UP: - var index; - if (!curSelectedIndex) { - if (options.enterEnabled()) { - index = false; - } else { - break; - } - } else { - index = curSelectedIndex - 1; - } - hover(index, true); - break; - case Keys.DOWN: - var index; - if (curSelectedIndex === false) { - index = 0; - } else { - index = curSelectedIndex + 1; - } - if (index > curResult.length - 1) { - break; - } - hover(index, true); - break; - default: - return; - } - e.stopImmediatePropagation(); - e.preventDefault(); - } - - function onKeyUp(e) { - clearTimeout(blurTimeout); - var value = $field.value(); - clearTimeout(keyUpTimeout); - if (curValue !== value) { - // if (e.type == 'keyup') { - // keyUpTimeout = setTimeout(function() { - // valueChange(); - // }, 50); - // } else { - options.onInputBeforeChange && options.onInputBeforeChange(value); - valueChange(); - options.onInput && options.onInput(value); - open(); - // } - } - } - - function onClick(e) { - open(); - } - - function check(item, queryLower) { - if (!queryLower.length) { - return 0; - } - for (var j = 0; j < item._values.length; j++) { - var valueLower = item._values[j]; - if (valueLower == queryLower) { - item._fullmatch = true; - return valueLower.length; - } - } - for (var j = 0; j < item._values.length; j++) { - var valueLower = item._values[j]; - var index = valueLower.indexOf(queryLower); - var found = options.prefixOnly ? index === 0 : index !== -1; - if (found) { - return valueLower.length; - } - } - return false; - } - - function search(data, query) { - var result = []; - result.fullMatchIndex = null; - if (!options.emptyQueryEnabled && !query.length) { - return result; - } - var time = +(new Date); - var queryLower = options.prepareQuery(query); - for (var i = 0; i < data.length; i++) { - var item = data[i]; - var valueScore = check(item, queryLower); - if (valueScore !== false) { - item._score = valueScore; - if (item._top) item._score -= 10000000; - else if (item._bottom) item._score += 10000000; - item._i = i; - result.push(item); - } - } - result.sort(function(item1, item2) { - return (item1._score - item2._score) || (item1._i - item2._i); - }); - for (i = 0; i < result.length; i++) { - var item = result[i]; - if (item._fullmatch) { - delete item._fullmatch; - if (result.fullMatchIndex === null) { - result.fullMatchIndex = i; - } - } - delete item._score; - delete item._i; - } - console.log('search: ' + (((new Date) - time) / 1000) + 's'); - return result; - } - - function render(result, query, from_index) { - if (from_index && from_index >= result.length) { - return; - } - var time = +(new Date); - var queryLower = options.prepareQuery(query); - from_index = from_index || 0; - var html = ''; - var render_limit = options.renderLimit || 50; - if (result.length > 0) { - for (var i = from_index, j = 0; i < result.length && j < render_limit; i++, j++) { - var item = result[i]; - var tagName = options.itemTagName || 'div'; - var className = 'search-item' + (options.itemClass ? ' ' + options.itemClass : '') + (item.className ? ' ' + item.className : ''); - var item_html = '<' + tagName + ' class="' + className + '" data-i="' + i + '">' + options.renderItem(item, query) + ''; - html += item_html; - } - curRenderedIndex = i; - } else { - html = options.renderNoItems ? options.renderNoItems(query) : ''; - curRenderedIndex = 0; - } - if (curRenderedIndex >= result.length) { - html += options.appendToItems ? options.appendToItems(query) : ''; - } - if (!result.length && html == '') { - options.$results.fadeHide(function() { - if (options.$results.isFadeHidden()) { - options.$results.html(html); - } - }); - } else { - if (options.$results.isFadeHidden()) { - options.$results.fadeShow(); - } - if (!from_index) { - options.$results.html(html); - } else if (html) { - options.$results.append(html); - } - } - updateScrollState(); - console.log('render: from ' + from_index + ', ' + j + ' lines, ' + (((new Date) - time) / 1000) + 's'); - } - - function renderLoading() { - curRenderedIndex = 0; - options.$results.html(options.renderLoading ? options.renderLoading() : ''); - updateScrollState(); - } - - function renderEmpty() { - curRenderedIndex = 0; - options.$results.html(''); - updateScrollState(); - } - - function close(no_anim) { - console.log(+new Date, 'close', no_anim); - clearTimeout(keyUpTimeout); - if (!options.$results.hasClass('collapsed')) { - if (options.$enter && options.enterEnabled()) { - options.$enter.removeClass('selected'); - } - if (no_anim) { - options.$results.animOff(); - } - options.$results.addClass('collapsed'); - options.onClose && options.onClose(); - if (no_anim) { - options.$results.animOn(); - } - } - } - - function open() { - clearTimeout(blurTimeout); - hover(curSelectedIndex, true); - if (options.$results.hasClass('collapsed')) { - options.$results.removeClass('collapsed'); - options.onOpen && options.onOpen(); - } - } - - function onFocus() { - isFocused = true; - var value = $field.value(); - if (curValue != value || - options.searchEnabled() && options.getData() === false) { - valueChange(); - } - open(); - } - - function onBlur() { - if (!isFocused) return; - console.log(+new Date, 'onblur'); - isFocused = false; - clearTimeout(blurTimeout); - blurTimeout = setTimeout(close, 100, false); - options.onBlur && options.onBlur(curValue); - } - - function valueChange() { - clearTimeout(blurTimeout); - clearTimeout(keyUpTimeout); - var value = $field.value(); - curValue = value; - console.log('valueChange', options.searchEnabled()); - if (options.searchEnabled()) { - var data = options.getData(); - if (data === false) { - if (!dataWaiting) { - dataWaiting = true; - $field.one('dataready.search', function() { - dataWaiting = false; - valueChange(); - }); - } - if (curValue.length || options.emptyQueryEnabled) { - renderLoading(); - } else { - renderEmpty(); - } - return; - } - curResult = search(data, curValue); - var index = false; - var $scrollableEl = options.resultsNotScrollable ? $(window) : options.$results; - $scrollableEl.scrollTop(0); - if (curValue.length || options.emptyQueryEnabled) { - render(curResult, curValue); - if (curResult.length && (!options.enterEnabled())) { - index = 0; - } - if (options.selectFullMatch && curResult.fullMatchIndex !== null) { - index = curResult.fullMatchIndex; - } - } else { - renderEmpty(); - } - } else { - curResult = []; - var index = false; - renderEmpty(); - } - hover(index, true); - } - - function hover(i, adjust_scroll, middle) { - $('.search-item.selected', options.$results).removeClass('selected'); - curSelectedIndex = i; - if (curSelectedIndex !== false) { - var selectedEl = $('.search-item', options.$results).get(curSelectedIndex); - if (!selectedEl) { - curSelectedIndex = false; - } else { - $(selectedEl).addClass('selected'); - if (adjust_scroll) { - adjustScroll($(selectedEl), middle); - } - if (Math.abs(curSelectedIndex - curRenderedIndex) < 5) { - render(curResult, curValue, curRenderedIndex); - } - } - } - if (options.$enter && options.enterEnabled()) { - options.$enter.toggleClass('selected', curSelectedIndex === false); - } - } - - function select(i) { - if (i === false) { - if (options.enterEnabled()) { - if (!options.noCloseOnEnter) { - $field.blur(); - } - options.onEnter && options.onEnter(curValue); - if (!options.noCloseOnEnter) { - close(true); - } - } - return; - } - if (!options.noCloseOnSelect) { - $field.blur(); - } - options.onSelect && options.onSelect(curResult[i]); - if (!options.noCloseOnSelect) { - close(true); - } - } - - function onItemHover() { - hover($(this).data('i'), true, true); - } - - function onItemMouseOver() { - hover($(this).data('i')); - } - - function updateScrollState() { - var results = options.$results.get(0); - if (results) { - options.$results.toggleClass('topscroll', results.scrollTop > 0); - options.$results.toggleClass('bottomscroll', results.scrollTop < results.scrollHeight - results.clientHeight); - } - } - - function onResultsScroll(e) { - updateScrollState(); - if (options.resultsNotScrollable) { - var bottom = options.$results.offset().top + options.$results.height() - $(window).scrollTop(); - if (bottom < $(window).height() * 2) { - render(curResult, curValue, curRenderedIndex); - } - } else { - if (this.scrollTop > this.scrollHeight - this.clientHeight - 1000) { - render(curResult, curValue, curRenderedIndex); - } - } - } - - function onItemClick(e) { - if (e.metaKey || e.ctrlKey) return true; - clearTimeout(blurTimeout); - e.stopImmediatePropagation(); - e.preventDefault(); - select($(this).data('i')); - } - - function adjustScroll($itemEl, middle) { - var scrollTop = options.$results.scrollTop(), - itemTop = $itemEl.position().top + scrollTop, - itemHeight = $itemEl.outerHeight(), - itemBottom = itemTop + itemHeight, - contHeight = options.$results.height() || 300; - - if (middle) { - options.$results.scrollTop(itemTop - (contHeight - itemHeight) / 2); - } else if (itemTop < scrollTop) { - options.$results.scrollTop(itemTop); - } else if (itemBottom > scrollTop + contHeight) { - options.$results.scrollTop(itemBottom - contHeight); - } - } - - if (options.$enter && options.enterEnabled()) { - options.$enter.on('mouseover.search', onItemMouseOver); - options.$enter.on('mousedown.search', onItemClick); - options.$enter.data('i', false); - } - options.$results.on('hover.search', '.search-item', onItemHover); - options.$results.on('mouseover.search', '.search-item', onItemMouseOver); - options.$results.on('mousedown.search', '.search-item', onItemClick); - if (options.resultsNotScrollable) { - $(window).on('scroll.search', onResultsScroll); - } else { - options.$results.on('scroll.search', onResultsScroll); - if (options.$results.isFixed()) { - options.$results.blockBodyScroll(); - } - } - if (options.initTextarea) { - $field.initTextarea(options.initTextarea); - } - $field.on('keydown.search', onKeyDown); - $field.on('keyup.search', onKeyUp); - $field.on('focus.search', onFocus); - $field.on('blur.search', onBlur); - $field.on('input.search', onKeyUp); - $field.on('click.search', onClick); - - $field.on('datachange.search', function() { - valueChange(); - }); - $field.on('contentchange.search', function() { - if (options.resultsNotScrollable) { - var scrolltop = $(window).scrollTop(); - } else { - var scrolltop = options.$results.scrollTop(); - } - var limit = options.renderLimit; - options.renderLimit = curRenderedIndex; - valueChange(); - options.renderLimit = limit; - if (options.resultsNotScrollable) { - $(window).scrollTop(scrolltop); - } else { - options.$results.scrollTop(scrolltop); - } - }); - - options.$results.addClass('collapsed'); - - if (options.updateOnInit) { - valueChange(); - } - return this; - }); - }; - $.fn.destroySearch = function() { - return this.map(function() { - var $field = $(this); - var options = $field.data('searchOptions'); - if (options) { - if (options.$enter && options.enterEnabled()) { - options.$enter.off('.search'); - } - options.$results.off('.search'); - if (options.resultsNotScrollable) { - $(window).off('.search'); - } - if (options.initTextarea) { - $field.destroyTextarea(); - } - } - $field.off('.search'); - return this; - }); - }; - $.fn.initSelect = function(options) { - return this.map(function() { - var $select = $(this); - var $field = $('.form-control', $select); - var $selected = $('.selected-items', $select); - var $results = $('.items-list', $select); - var selectedVal = [], selectedMap = {}; - - $select.data('options', options); - - function getValue(full) { - if (options.multiSelect) { - return full ? $.extend({}, selectedMap) : [].concat(selectedVal); - } else { - return selectedVal.length > 0 ? (full ? selectedMap[selectedVal[0]] : selectedVal[0]) : (full ? false : ''); - } - } - function setValue() { - var selValue = getValue(), selValueFull = getValue(true); - $select.data('value', selValue); - $select.data('valueFull', selValueFull); - options.onChange && options.onChange(selValue, selValueFull); - } - - function toggleDD(open) { - $select.toggleClass('open', open); - } - function addSelected(item, noupdate) { - var val = (item.prefix || '') + item.val; - if (!selectedMap[val]) { - if (!options.multiSelect) { - for (var i = 0; i < selectedVal.length; i++) { - delete selectedMap[selectedVal[i]]; - } - selectedVal = []; - } - else if (item.group) { - for (var i = selectedVal.length - 1; i >= 0; i--) { - if (selectedMap[selectedVal[i]].group == item.group) { - delete selectedMap[selectedVal[i]]; - selectedVal.splice(i, 1); - } - } - } - selectedVal.push(val); - selectedMap[val] = item; - if (!noupdate) { - setValue(); - updateSelected(); - } - } - } - function delSelected(val) { - if (selectedMap[val]) { - delete selectedMap[val]; - for (var i = 0; i < selectedVal.length; i++) { - if (selectedVal[i] == val) { - selectedVal.splice(i, 1); - break; - } - } - setValue(); - updateSelected(); - } - } - function clearSelected() { - for (var i = 0; i < selectedVal.length; i++) { - var val = selectedVal[i]; - delete selectedMap[val]; - } - selectedVal = []; - setValue(); - updateSelected(); - } - function updateSelected() { - var html = ''; - for (var i = 0; i < selectedVal.length; i++) { - var val = selectedVal[i]; - var item = selectedMap[val]; - html += options.renderSelectedItem ? options.renderSelectedItem(val, item) : '
' + item.name + '
'; - } - $('.selected-item', $selected).remove(); - $selected.prepend(html); - options.onUpdate && options.onUpdate(getValue(), getValue(true)); - } - - var initTextarea = null; - var isContentEditable = $field.is('[contenteditable]'); - if (isContentEditable) { - initTextarea = options.noSearch ? { - singleLine: true, - checkText: function() { return ''; } - } : { - singleLine: true - }; - } - $field.initSearch($.extend({ - $results: $results, - emptyQueryEnabled: true, - noCloseOnSelect: options.multiSelect, - updateOnInit: true, - renderItem: function(item) { - return '
' + item.name + '
'; - }, - prepareQuery: function(str) { - str = str.toLowerCase(); - if (options.searchByLastWord) { - str = str.split(/\s+/).pop(); - } - return str; - }, - onOpen: function() { - toggleDD(true); - }, - onClose: function() { - toggleDD(false); - } - }, options, { - getData: function() { - var data = options.getData(); - if (data === false) { - return false; - } - var filtered_data = []; - for (var i = 0; i < data.length; i++) { - if (data[i].hidden) continue; - var val = (data[i].prefix || '') + data[i].val; - if (!selectedMap[val] || !options.multiSelect) { - filtered_data.push(data[i]); - } - } - return filtered_data; - }, - onSelect: function(item) { - var newValue = ''; - if (options.searchByLastWord) { - var oldValue = $field.value(); - var lastWord = oldValue.split(/\s+/).pop(); - newValue = oldValue; - if (lastWord.length > 0) { - newValue = oldValue.substr(0, oldValue.length - lastWord.length); - } - newValue = newValue.replace(/^\s+/, ''); - } - $field.value(newValue); - addSelected(item); - if (options.multiSelect) { - $field.trigger('contentchange').focusAndSelect(); - } - }, - initTextarea: initTextarea - })); - if (options.noSearch) { - $select.addClass('no-search'); - } - if (!isContentEditable) { - $field.on('keydown.select', function(e) { - if (!e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey && - (e.which == Keys.BACKSPACE) && - this.selectionStart == this.selectionEnd && - !this.selectionStart) { - $(this).trigger('backspaceonleft'); - } - }); - } - var defValue = $select.defaultValue(); - var defSelected = defValue.length ? defValue.split(';') : [], dataMap = {}; - if (defSelected.length) { - var data = options.getData(); - if (data !== false) { - for (var i = 0; i < data.length; i++) { - var val = (data[i].prefix || '') + data[i].val; - dataMap[val] = data[i]; - } - } - for (var i = 0, item; i < defSelected.length; i++) { - if (item = dataMap[defSelected[i]]) { - addSelected(item, true); - } - } - } - $select.data('value', getValue()); - $select.data('valueFull', getValue(true)); - $select.on('selectval.select', function(e, val, clear) { - addSelected(val); - if (clear) { - $field.value(''); - } - $field.trigger('datachange'); - }); - $select.on('deselectval.select', function(e, val) { - delSelected(val); - $field.trigger('datachange'); - }); - $select.on('reset.select', function(e) { - $('.selected-item', $selected).each(function() { - var val = $(this).attr('data-val'); - delSelected(val); - }); - $field.trigger('datachange'); - }); - $field.on('backspaceonleft.select', function(e) { - if (options.focusSelectedBeforeDelete) { - var $focused = $('.selected-item.focused', $selected); - if ($focused.size() > 0) { - var val = $focused.eq(-1).attr('data-val'); - delSelected(val); - $field.trigger('datachange'); - } else { - $('.selected-item', $selected).eq(-1).addClass('focused'); - } - } else { - var $items = $('.selected-item', $selected); - if ($items.size() > 0) { - var val = $items.eq(-1).attr('data-val'); - delSelected(val); - $field.trigger('datachange'); - } - } - }); - $field.on('focus.select', function() { - $('.selected-item.focused', $selected).removeClass('focused'); - }); - $selected.on('click.select', '.selected-item', function(e) { - $('.selected-item.focused', $selected).removeClass('focused'); - $(this).addClass('focused'); - e.stopImmediatePropagation(); - }); - $selected.on('click.select', '.selected-item .close', function(e) { - var val = $(this).parents('.selected-item').attr('data-val'); - delSelected(val); - if (options.multiSelect) { - $field.trigger('datachange').focusAndSelectAll(); - } - e.stopImmediatePropagation(); - }); - $select.on('click.select', '.select-clear', function(e) { - if ($field.value().length > 0) { - $field.value('').trigger('input').focus(); - options.onClear && options.onClear(); - } else { - clearSelected(); - $field.focus(); - } - e.stopImmediatePropagation(); - }); - $select.on('click.select', function(e) { - if ($(e.target).is('.select')) { - $field.focus(); - } - }); - if (selectedVal.length) { - updateSelected(); - $field.trigger('datachange'); - } - $select.data('inited', true); - return this; - }); - }; - $.fn.destroySelect = function() { - return this.map(function() { - var $select = $(this); - var $field = $('.form-control', $select); - var $selected = $('.selected-items', $select); - $field.destroySearch(); - $field.off('.select'); - $selected.off('.select'); - return this; - }); - } - $.fn.hasField = function(name) { - return this.first().map(function() { - if (this.tagName == 'FORM') { - if (this[name]) { - return true; - } - return $('.input[data-name]', this).filter(function() { - return ($(this).attr('data-name') == name); - }).size() > 0; - } - return false; - }).get(0) || false; - }; - $.fn.field = function(name) { - return this.first().map(function() { - if (this.tagName == 'FORM') { - if (this[name]) { - return this[name]; - } - return $('.input[data-name],.select[data-name]', this).filter(function() { - return ($(this).attr('data-name') == name); - }).get(0); - } - }); - }; - $.fn.reset = function(val) { - return this.each(function() { - if (this.tagName == 'FORM') { - this.reset(); - $('.input[data-name]', this).each(function() { - $(this).text($(this).attr('data-value')).trigger('input'); - }); - $('.select[data-name]', this).each(function() { - $(this).trigger('reset'); - }); - } - }); - }; - $.fn.scrollHeight = function() { - return this.first().map(function() { - return this.scrollHeight; - }).get(0) || ''; - }; - $.fn.defaultValue = function(val) { - if (typeof val !== 'undefined') { - return this.each(function() { - if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT') { - this.defaultValue = val; - } else { - $(this).attr('data-value', val); - } - }); - } - return this.first().map(function() { - if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT') { - return this.defaultValue || ''; - } else { - return $(this).attr('data-value') || ''; - } - }).get(0) || ''; - }; - $.fn.value = function(val) { - if (typeof val !== 'undefined') { - return this.each(function() { - if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT' || this instanceof RadioNodeList) { - this.value = val; - } else { - $(this).text(val).trigger('input'); - } - }); - } - return this.first().map(function() { - if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT' || this instanceof RadioNodeList) { - return this.value || ''; - } else { - return $(this).text() || ''; - } - }).get(0) || ''; - }; - $.fn.values = function(val) { - if (typeof val !== 'undefined') { - return this.value(val); - } - return this.map(function() { - if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT') { - return this.value || ''; - } else { - return $(this).text() || ''; - } - }).get() || []; - }; - - $.fn.initTextarea = function(options) { - options = options || {}; - - function getRangeText(range) { - var div = document.createElement('DIV'); - div.appendChild(range.cloneContents()); - return getText(div, true); - } - function isBlockEl(el) { - var blockTags = {ADDRESS: 1, ARTICLE: 1, ASIDE: 1, AUDIO: 1, BLOCKQUOTE: 1, CANVAS: 1, DD: 1, DIV: 1, DL: 1, FIELDSET: 1, FIGCAPTION: 1, FIGURE: 1, FIGURE: 1, FIGCAPTION: 1, FOOTER: 1, FORM: 1, H1: 1, H2: 1, H3: 1, H4: 1, H5: 1, H6: 1, HEADER: 1, HGROUP: 1, HR: 1, LI: 1, MAIN: 1, NAV: 1, NOSCRIPT: 1, OL: 1, OUTPUT: 1, P: 1, PRE: 1, SECTION: 1, TABLE: 1, TFOOT: 1, UL: 1, VIDEO: 1}; - // return (el.nodeType == el.ELEMENT_NODE && blockTags[el.tagName]); - if (el.nodeType == el.ELEMENT_NODE) { - var display = $(el).css('display'); - if (!display) return blockTags[el.tagName]; - return (display == 'block' || display == 'table' || display == 'table-row'); - } - return false; - } - function isMetadataEl(el) { - var metadataTags = {HEAD: 1, TITLE: 1, BASE: 1, LINK: 1, META: 1, STYLE: 1, SCRIPT: 1}; - return (el.nodeType == el.ELEMENT_NODE && metadataTags[el.tagName]); - } - function getText(el, safe_last_br) { - var child = el.firstChild, blocks = [], block = ''; - while (child) { - if (child.nodeType == child.TEXT_NODE) { - block += child.nodeValue; - } else if (child.nodeType == child.ELEMENT_NODE && !isMetadataEl(child)) { - if (child.tagName == 'BR') { - block += '\n'; - } else if (child.tagName == 'IMG') { - block += child.getAttribute('alt') || ''; - } else if (!isBlockEl(child)) { - block += getText(child); - } else { - if (block.length > 0) { - if (block.substr(-1) == '\n') { - block = block.slice(0, -1); - } - blocks.push(block); - block = ''; - } - blocks.push(getText(child, safe_last_br)); - } - } - child = child.nextSibling; - } - if (block.length > 0) { - if (!safe_last_br && block.substr(-1) == '\n') { - block = block.slice(0, -1); - } - blocks.push(block); - } - return blocks.join('\n'); - } - function getTextNodesIn(node) { - var textNodes = []; - if (node.nodeType == node.TEXT_NODE) { - textNodes.push(node); - } else { - for (var i = 0, len = node.childNodes.length; i < len; ++i) { - textNodes.push.apply(textNodes, getTextNodesIn(node.childNodes[i])); - } - } - return textNodes; - } - function editableClosest(el) { - while (el) { - if (el.nodeType == el.ELEMENT_NODE && - el.getAttribute('contenteditable') == 'true') { - return el; - } - el = el.parentNode; - } - return null; - } - function nonEditableClosest(el) { - while (el) { - if (el.tagName == 'MARK' && - el.getAttribute('contenteditable') == 'false') { - return el; - } - el = el.parentNode; - } - return null; - } - function setSelectionRange(el, start, end) { - var sel = window.getSelection(); - sel.removeAllRanges(); - var textNodes = getTextNodesIn(el); - var charCount = 0, endCharCount, i, textNode, node, offset, nonEditEl; - for (i = 0, charCount = 0; textNode = textNodes[i++]; ) { - endCharCount = charCount + textNode.length; - if (start >= charCount && (start < endCharCount || - (start == endCharCount && i <= textNodes.length))) { - if (nonEditEl = nonEditableClosest(textNode)) { - var range = document.createRange(); - if (start < end) range.setStartBefore(nonEditEl); - else range.setStartAfter(nonEditEl); - node = range.startContainer; - offset = range.startOffset; - } else { - node = textNode; - offset = start - charCount; - } - sel.collapse(node, offset); - break; - } - charCount = endCharCount; - } - if (start != end) { - for (i = 0, charCount = 0; textNode = textNodes[i++]; ) { - endCharCount = charCount + textNode.length; - if (end >= charCount && (end < endCharCount || - (end == endCharCount && i <= textNodes.length))) { - if (nonEditEl = nonEditableClosest(textNode)) { - var range = document.createRange(); - if (start < end) range.setStartAfter(nonEditEl); - else range.setStartBefore(nonEditEl); - node = range.startContainer; - offset = range.startOffset; - } else { - node = textNode; - offset = end - charCount; - } - sel.extend(node, offset); - break; - } - charCount = endCharCount; - } - } - } - function onKeyDown(e) { - if ((e.metaKey || e.ctrlKey) && !e.altKey && - e.which == 90) { // Z - e.preventDefault(); - if (e.shiftKey) { - redo(this); - } else { - undo(this); - } - } - else if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && - e.which == 89) { // Y - e.preventDefault(); - redo(this); - } - else if (!e.shiftKey && !e.altKey && e.which == 13) { // Enter - if ((e.metaKey || e.ctrlKey) || $(this).data('textOptions').singleLine) { - e.preventDefault(); - $(this).parents('form').submit(); - } - } - else if ((e.metaKey || e.ctrlKey) && - !e.shiftKey && !e.altKey && e.which == 73 && - $(this).data('textOptions').allowTokens) { // I - e.preventDefault(); - $(this).data('$tokens').filter(':not(.used)').eq(0).trigger('click'); - } - else if (!e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey && - (e.which == Keys.LEFT || e.which == Keys.RIGHT || e.which == Keys.BACKSPACE)) { - var isLeft = e.which == Keys.LEFT || e.which == Keys.BACKSPACE; - var isBackspace = e.which == Keys.BACKSPACE; - var sel = window.getSelection(); - if (sel.isCollapsed && sel.focusNode) { - if (sel.focusNode.nodeType == sel.focusNode.TEXT_NODE) { - var newOffset = sel.focusOffset + (isLeft ? -1 : 1); - if (newOffset < 0) { - var prevNode = sel.focusNode.previousSibling; - if (prevNode && prevNode.nodeType == prevNode.ELEMENT_NODE) { - var range = document.createRange(); - range.setStartBefore(prevNode); - if (isBackspace) { - range.setEnd(sel.focusNode, sel.focusOffset); - range.deleteContents(); - $(sel.focusNode).closest('.input').trigger('input'); - } else { - sel.collapse(range.startContainer, range.startOffset); - } - e.preventDefault(); - } else { - if (isBackspace) { - $(sel.focusNode).closest('.input').trigger('backspaceonleft'); - } - } - } else if (newOffset > sel.focusNode.nodeValue.length) { - var nextNode = sel.focusNode.nextSibling; - if (nextNode.nodeType == nextNode.ELEMENT_NODE && nextNode.tagName != 'BR') { - var range = document.createRange(); - range.setStartAfter(nextNode); - if (!isBackspace) { - sel.collapse(range.startContainer, range.startOffset); - } - e.preventDefault(); - } - } - } - else if (sel.focusNode.nodeType == sel.focusNode.ELEMENT_NODE) { - var curNode = sel.focusNode.childNodes[sel.focusOffset]; - if (isLeft) { - var prevNode = curNode ? curNode.previousSibling : sel.focusNode.lastChild; - while (prevNode && - prevNode.nodeType == prevNode.TEXT_NODE && - !prevNode.nodeValue.length) { - prevNode = prevNode.previousSibling; - } - if (prevNode && prevNode.nodeType == prevNode.ELEMENT_NODE) { - if (isBackspace) { - var range = document.createRange(); - range.selectNode(prevNode); - range.deleteContents(); - $(sel.focusNode).closest('.input').trigger('input'); - } else { - sel.collapse(sel.focusNode, sel.focusOffset - 1); - } - e.preventDefault(); - } else if (prevNode && prevNode.nodeType == prevNode.TEXT_NODE) { - if (isBackspace) { - var range = document.createRange(); - range.setStart(prevNode, prevNode.nodeValue.length - 1); - range.setEnd(prevNode, prevNode.nodeValue.length); - range.deleteContents(); - $(sel.focusNode).closest('.input').trigger('input'); - } else { - sel.collapse(prevNode, prevNode.nodeValue.length - 1); - } - e.preventDefault(); - } else { - if (isBackspace) { - $(sel.focusNode).closest('.input').trigger('backspaceonleft'); - } - } - } else { - if (curNode && curNode.nodeType == curNode.ELEMENT_NODE && curNode.tagName != 'BR') { - sel.collapse(sel.focusNode, sel.focusOffset + 1); - e.preventDefault(); - } else if (curNode && curNode.nodeType == curNode.TEXT_NODE) { - sel.collapse(curNode, 1); - e.preventDefault(); - } - } - } - } - } - } - function getFieldRange(field) { - var sel = window.getSelection(); - if (sel.anchorNode && sel.focusNode) { - var rng = document.createRange(); - rng.setStart(field, 0); - rng.setEnd(sel.anchorNode, sel.anchorOffset); - var startOffset = getRangeText(rng).length; - rng.setEnd(sel.focusNode, sel.focusOffset); - var endOffset = getRangeText(rng).length; - return {startOffset: startOffset, endOffset: endOffset}; - } - var offset = field.childNodes.length; - if (field.lastChild && field.lastChild.tagName == 'BR') { - offset--; - } - return {startOffset: offset, endOffset: offset}; - } - function setFieldRange(field, fieldRange) { - if (fieldRange) { - setSelectionRange(field, fieldRange.startOffset, fieldRange.endOffset); - } - } - function onSetFocus() { - setFieldRange(this, $(this).data('prevSelRange')); - } - function update(field, text, fieldRange) { - var $field = $(field); - var tokens = $field.data('tokens'); - var options = $field.data('textOptions'); - if (options.checkText) { - text = options.checkText(text); - } - var html = cleanHTML(text), fhtml; - if (options.allowTokens) { - var avail_tokens = []; - $.each(tokens, function(i, value) { - avail_tokens[i] = cleanHTML(value); - }); - var avail_count = tokens.length; - var $tokens = $field.data('$tokens'); - if (avail_count > 0) { - html = html.replace(TOKEN_REGEX, function(s) { - var i = avail_tokens.indexOf(s); - if (i >= 0) { - avail_tokens[i] = null; - avail_count--; - var $token = $tokens.eq(i); - if (!$token.hasClass('used')) { - $token.prepareSlideX().addClass('used'); - } - return '' + s + ''; - } else { - return s; - } - }); - $tokens.each(function(i) { - if (avail_tokens[i] !== null) { - var $token = $(this); - if ($token.hasClass('used')) { - $token.prepareSlideX().removeClass('used'); - } - } - }); - } - $tokens.parents('.key-add-tokens-wrap').toggleClass('empty', !avail_count) - } - if (options.allowEmoji && options.emojiRE) { - html = html.replace(options.emojiRE, function(s) { - return '' + EmojiSearch.emojiHtml(s) + ''; - }); - } - html = html.split(getBR()).join('\n'); - if (options.singleLine) { - html = html.replace(/^\n+|\n+$/g, '').replace(/\n+/g, ' '); - } - fhtml = $field.html(); - if (fhtml === html) { - $field.append('
').toggleClass('empty', !$field.text().length); - return; - } - if (fhtml === html + getBR()) { - $field.toggleClass('empty', !$field.text().length); - return; - } - - fieldRange = fieldRange || getFieldRange(field); - $field.html(html + getBR()).toggleClass('empty', !$field.text().length); - setFieldRange(field, fieldRange); - } - function onInput() { - var field = this; - var $field = $(this); - var text = getText(field); - update(field, text); - - var history = $field.data('history'); - var fieldRange = getFieldRange(field); - var prevSelRange = $field.data('prevSelRange'); - var time = +(new Date); - history.list = history.index >= 0 ? history.list.slice(0, history.index + 1) : []; - if (history.index >= 0 && history.list[history.index]) { - var entry = history.list[history.index]; - if (entry.text == text) { - return; - } - if (time - entry.time < 1000 && - entry.redoSel.startOffset == entry.redoSel.endOffset && - (entry.text.length - entry.redoSel.endOffset) == - (text.length - fieldRange.endOffset)) { - entry.text = text; - entry.redoSel = fieldRange; - return; - } - entry.undoSel = prevSelRange; - } - history.list.push({text: text, redoSel: fieldRange, time: time}); - history.index++; - } - function undo(field) { - var $field = $(field); - var history = $field.data('history'); - if (history.index > 0) { - history.index--; - var entry = history.list[history.index]; - update(field, entry.text, entry.undoSel); - } - } - function redo(field) { - var $field = $(field); - var history = $field.data('history'); - if (history.index < history.list.length - 1) { - history.index++; - var entry = history.list[history.index]; - update(field, entry.text, entry.redoSel); - } - } - function onSelectionChange() { - $(this).data('prevSelRange', getFieldRange(this)); - var sel = window.getSelection(); - if (sel.isCollapsed) { - var nonEditEl; - if (nonEditEl = nonEditableClosest(sel.focusNode)) { - var range = document.createRange(); - if (sel.focusOffset < $(nonEditEl).text().length / 2) { - range.setStartBefore(nonEditEl); - } else { - range.setStartAfter(nonEditEl); - } - sel.collapse(range.startContainer, range.startOffset); - } - else if (sel.focusNode === this && sel.focusOffset == this.childNodes.length && this.lastChild && this.lastChild.nodeType == 'BR') { - sel.collapse(this, this.childNodes.length - 1); - } - else if (sel.focusNode.nodeType == sel.focusNode.TEXT_NODE && sel.focusOffset == sel.focusNode.nodeValue.length) { - var range = document.createRange(); - range.setStartAfter(sel.focusNode); - sel.collapse(range.startContainer, range.startOffset); - } - } - } - - if (!$(document).data('selectionchange_inited')) { - $(document).data('selectionchange_inited', true); - document.execCommand('autoUrlDetect', false, false); - $(document).on('selectionchange', function() { - var sel = window.getSelection(); - var anchorField, focusField; - var field, offset; - if (sel.anchorNode && (anchorField = editableClosest(sel.anchorNode))) { - $(anchorField).triggerHandler('selectionchange'); - } - if (sel.focusNode && (focusField = editableClosest(sel.focusNode)) && - anchorField != focusField) { - $(focusField).triggerHandler('selectionchange'); - } - if (!sel.focusNode && - document.activeElement && - document.activeElement.getAttribute('contenteditable') == 'true') { - field = document.activeElement; - offset = field.childNodes.length; - if (field.lastChild.tagName == 'BR') { - offset--; - } - sel.collapse(field, offset); - } - }); - } - - return this.each(function() { - var field = this; - var $field = $(field); - var textOptions = $.extend({}, options); - if ($field.data('inited')) { - return; - } - $field.attr('contenteditable', 'true'); - $field.data('textOptions', textOptions); - - function insertTag(e) { - e.preventDefault(); - document.execCommand('insertText', false, $(this).attr('data-token')); - $field.focus(); - } - - $field.data('history', {list: [], index: -1}); - - if (options.allowTokens) { - var tokens_attr = $field.attr('data-tokens'); - var tokens = tokens_attr ? tokens_attr.split(' ') : []; - - var $tokensBtns = $('
'); - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i] = tokens[i].replace('\xa0', ' '); - var $token = $(''); - $token.attr('data-token', token).appendTo($tokensBtns); - } - var ua = navigator.userAgent || '', - is_mac = ua.indexOf('Mac') >= 0 || - ua.indexOf('AppleWebKit') >= 0 && - /Mobile\/\w+/.test(ua); - var shortcut = is_mac ? '⌘I' : 'Ctrl+I'; - $tokensBtns.attr('data-shortcut', shortcut).wrap('
').parent().wrap('
').parent().toggleClass('empty', !tokens.length).insertAfter($field); - var $tokens = $('.field-ins-btn', $tokensBtns); - $tokens.on('click.tr-textarea', insertTag); - $field.data('$tokens', $tokens); - $field.data('tokens', tokens); - } - if ($field.is('[data-single-line]')) { - textOptions.singleLine = true; - } - if ($field.is('[data-value]')) { - $field.value($field.defaultValue()); - } else { - $field.defaultValue($field.value()); - } - - $field.on('selectionchange.tr-textarea', onSelectionChange); - $field.on('keydown.tr-textarea', onKeyDown); - $field.on('input.tr-textarea', onInput); - $field.on('setfocus.tr-textarea', onSetFocus); - $field.trigger('input'); - $field.data('inited', true); - }); - - }; - $.fn.destroyTextarea = function() { - return this.off('.tr-textarea').each(function() { - $(this).data('inited', false); - var $tokens = $(this).data('$tokens'); - if ($tokens) { - $tokens.off('.tr-textarea'); - } - }); - }; - - $.fn.blockBodyScroll = function() { - function onResultsMouseWheel(e) { - var d = e.originalEvent.wheelDelta; - if((this.scrollTop === (this.scrollHeight - this.clientHeight) && d < 0) || - (this.scrollTop === 0 && d > 0)) { - e.preventDefault(); - } - } - return this.on('mousewheel', onResultsMouseWheel); - }; - - $.fn.initAutosize = function() { - return this.map(function(){ autosize(this); return this; }); - }; - - $.fn.updateAutosize = function() { - return this.map(function(){ autosize.update(this); return this; }); - }; - - $.fn.destroyAutosize = function() { - return this.map(function(){ autosize.destroy(this); return this; }); - }; - -})(jQuery); - -function getBR() { - if (window._brHTML) return window._brHTML; - return window._brHTML = $('

').html(); -} - -function cleanHTML(value) { - return value.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/\n/g, getBR()); -} -function uncleanHTML(value) { - return $('
').html(value).text(); -} -function cleanRE(value) { - return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); -} -function wrapHighlight(value, highlight, wrap_tag, prefix_only) { - value = cleanHTML(value); - if (highlight) { - var pattern = cleanRE(cleanHTML(highlight)); - if (prefix_only) { - pattern = '^' + pattern; - } - value = value.replace(new RegExp(pattern, 'gi'), '$&<\/strong>'); - } - if (wrap_tag) { - value = value.replace(TOKEN_REGEX, '$&'); - } - return value; -} -function wrapSize(size) { - if (size < 1024) { - return size + ' B'; - } else if (size < 1048576) { - return (Math.round(size * 10 / 1024.0) / 10) + ' KB'; - } else if (size < 1073741824) { - return (Math.round(size * 10 / 1048576.0) / 10) + ' MB'; - } else { - return (Math.round(size * 10 / 1073741824.0) / 10) + ' GB'; - } -} -function dataUrlToBlob(url) { - try { - var match = null; - if (match = url.match(/^data:(image\/gif|image\/jpe?g|image\/png|video\/mp4);base64,(.*)$/)) { - var type = match[1], b64 = match[2]; - var binary = atob(b64); - var array = []; - for(var i = 0; i < binary.length; i++) { - array.push(binary.charCodeAt(i)); - } - return new Blob([new Uint8Array(array)], {type: type}); - } - } catch (e) {} - return false; -} -function copyToClipboard(str) { - var $text = $('