def append_chat_message(self, sender, message, color): self.chat_log_label.text += "[b](%s) [color=%s]%s[/color][/b]: %s\n" % ( datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), color, escape_markup(sender), escape_markup(message)) self.chat_log_label.parent.scroll_y = 0.0
def append_chat_message(self, sender, message, color): self.chat_log_label.text += "[b](%s) [color=%s]%s[/color][/b]: %s\n" % ( datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), color, escape_markup(sender), escape_markup(message)) self.chat_log_label.parent.scroll_y = 0.0
def color_orp_char(self, word, orp): """Change color of the ORP letter in ``word`` to red. :param word: the word to be color-coded :type word: ``unicode`` :param orp: the index of the ORP letter :type orp: ``integer`` :returns: word with ORP letter in red :rytpe: ``unicode`` """ color_red = '[color=ff3333]' color_restore = '[/color]' return escape_markup(word[0:orp]) \ + color_red + escape_markup(word[orp:orp+1]) \ + color_restore + escape_markup(word[orp+1:])
def highlight_player(self): self._remove_player_info() if self._sDeck: label = Label( text="[b][color=00ffff]%s [i](playing %s)[/i]" "[/color][/b]" % (escape_markup(self._sPlayer), escape_markup(self._sDeck)), markup=True) else: label = Label(text="[b][color=00ffff]%s [i](unspecfied)" "[/i][/color][/b] " % escape_markup(self._sPlayer), markup=True) self.player.add_widget(label) self._add_turn_info() self._update_game()
def highlight_player(self): self._remove_player_info() if self._sDeck: label = Label(text="[b][color=00ffff]%s [i](playing %s)[/i]" "[/color][/b]" % (escape_markup(self._sPlayer), escape_markup(self._sDeck)), markup=True) else: label = Label(text="[b][color=00ffff]%s [i](unspecfied)" "[/i][/color][/b] " % escape_markup( self._sPlayer), markup=True) self.player.add_widget(label) self._add_turn_info() self._update_game()
def update_playlist(self): playlist_aux2 = '' for i in self.karaoke.playlist: playlist_aux = playlist_aux2 + '[size=20][color=#FFFFFF][font=NotoSansCJKjp-Medium.otf]' + escape_markup( i['Title']) + '[/color][/size][/font]' + '\n' playlist_aux = playlist_aux + '[size=15][color=#c5cacd][font=NotoSansCJKjp-Medium.otf]' + escape_markup( i['Artist']) + ' ' + escape_markup( '[') + self.karaoke.format_seconds( i['Length']) + escape_markup( ']') + '[/color][/size][/font]' + '\n\n' playlist_aux2 = playlist_aux self.playlist_list_prop.text = playlist_aux2
def append_message(self, msg): msg = preprocess(msg, (replace_emotes, remove_formatting)) msg = links_to_markup(escape_markup(msg)) self._append(msg) self.container.height = self._container_height() self.children[0].scroll_y = 0
def send_message(self, *args): if len(self.text) > 250: popup = MOPopup("Warning", "Message too long", "OK") popup.open() return elif len(self.text) == 0 and not self.icon_change_spam: self.icon_change_spam = True Clock.schedule_once(self.enable_icon_change, 0.25) App.get_running_app().get_user_handler().send_icon() return main_scr = App.get_running_app().get_main_screen() Clock.schedule_once(main_scr.refocus_text) msg = escape_markup(self.text) if not self.message_is_valid(msg): return self.text = "" msg = self.extend_message(msg) if self.message_is_command(msg): try: self.handle_command(msg) except (AttributeError, IndexError) as e: Logger.warning(traceback.format_exc()) return else: user_handler = App.get_running_app().get_user_handler() try: user_handler.send_message(msg) except Exception as e: popup = MOPopup("Warning", "Something went wrong. " + str(e), "OK") popup.open()
def send_error(error): send_log_line(u'[b]Во время работы произошла непредвиденная ошибка![/b]', 2) send_log_line(escape_markup(error.decode('unicode-escape')), 2) utils.save_error(error, from_bot=True) send_log_line(u'[b]Ошибка сохранена в файле {bot_error_file}[/b]', 2)
def update_ooc(self, msg, sender): ref = msg if sender == 'default': sender = App.get_running_app().get_user().username if 'www.' in msg or 'http://' in msg or 'https://' in msg: msg = "[u]{}[/u]".format(msg) if self.counter == 100: self.counter = 0 self.ooc_chat = OOCLogLabel() self.chat_grid.add_widget(self.ooc_chat) main_scr = App.get_running_app().get_main_screen() self.ooc_chat.bind(on_ref_press=main_scr.log_window.copy_text) self.ooc_chat.text += "{0}: [ref={2}]{1}[/ref]\n".format( sender, msg, escape_markup(ref)) self.counter += 1 config = App.get_running_app().config if config.getdefaultint('other', 'ooc_scrolling', 1): self.ooc_chat.parent.parent.scroll_y = 0 now = datetime.now() cur_date = now.strftime("%d-%m-%Y") cur_time = now.strftime("%H:%M:%S") log_msg = "<{} {}> {}: {}\n".format(cur_time, cur_date, sender, msg) with open('ooc_log.txt', 'a', encoding='utf-8') as f: f.write(log_msg) if self.current_tab != self.ooc_chat_header: color = [0, 0.5, 1, 1] if self.ooc_chat_header.background_color != color: self.ooc_chat_header.background_normal = '' self.ooc_chat_header.background_color = color if self.ooc_play: self.ooc_notif.play() config = App.get_running_app().config delay = config.getdefaultint('other', 'ooc_notif_delay', 60) Clock.schedule_once(self.ooc_time_callback, delay) self.ooc_play = False
def refresh_view_attrs(self, rv, index, data): self.index = index self.font_name = rv.font_name self.font_size = rv.font_size text = data['text'] if text: len_text = len(text) opening = 0 for i in range(len_text): opening = text[opening:].find('[') + opening if opening == -1: break else: is_closing = text[opening:].find(']') if is_closing == -1: text = '%s%s%s' % ( text[:opening], escape_markup(text[opening:opening+2]), text[opening+2:]) opening += 2 if text[0] == '#': col = get_hex_from_color((0.7, 0.7, 0.3, 1.0)) text = '[color=%s]%s[/color]' % (col, text) else: text = self.markup_log_level(text) text = self.markup_important_words(text, len_text) for attr, value in data.items(): if attr != 'text': setattr(self, attr, value) self.text = text
def _on_textedit(self, _, value): ''' when there is nothing to be acquired, 'getEnterdSting','getComposition','getCandidate' function returns '\n\n'. (It's because I think that IME will not returns '\n\n') valiable of value can only retains about 15 charactors. But 'on_textedit' event is fired when size of composition string exceeds 15 too. So when fired the event,this function does processing such as acquistion candidates. 返すべき値がないとき、 'getEnterdSting','getComposition','getCandidate' これらの関数は、'\n\n'という文字列を返します。(IMEからこの文字列が 取得されることがあるとは考えられないからです) なおSDL2の制約により、この関数の引数であるvalueには15文字程度以上 は保持出来ません。しかし、texteditイベント自体は入力中の文字が15文字 を超えても発火されるため、このイベントが呼ばれたタイミングで 変換候補取得などの処理をしています。 ''' self.sdl_composition = value self.is_openIME = bool(dll.getIsOpenIME()) try: entered_text = dll.getEnterdString().decode('cp932') composition_string = dll.getComposition().decode('cp932') candidates = dll.getCandidate().decode('cp932').split() except UnicodeError: print('failed to decode IME information') escaped_text = '\n'.join( [f'[ref={escape_markup(i)}]' + escape_markup(i) + '[/ref]' for i in candidates] ) self.candidate_window.escaped_text = escaped_text self.candidate_window.text = escaped_text if composition_string != '\n\n': self.composition_string = composition_string else: self.composition_string = '' if (entered_text != '\n\n' and self.is_openIME and self.old_composition != value): index = self.cursor_index() self.text = self.text[:index - 1] + \ entered_text + self.text[index:] self.composition_string = '' self.old_composition = value return None self.old_composition = value
def _flush(self, end): if end < 0: # end may be negative value, when trying to flush after first text part is token return text_to_print = self._original_text[self._printed_character + 1:end] self._output_text.append(escape_markup(text_to_print)) self._printed_character = end
def update_conversation(self, sender, msg): self.set_current_conversation_user(sender) if 'www.' in msg or 'http://' in msg or 'https://' in msg: msg = "[u]{}[/u]".format(msg) self.current_conversation.msgs += "{0}:[ref={2}]{1}[/ref]\n".format( sender, msg, escape_markup(msg)) self.pm_body.clear_widgets() self.update_pms()
def showPopup(title, message): popupWindow = Popup(title=title, content=Label(text='[b]' + escape_markup(message) + '[/b]', markup=True), size_hint=(None, None), size=(400, 400)) popupWindow.open()
def _handle_color(self, node: JSONMessagePart): colors = node["color"].split(";") node["text"] = escape_markup(node["text"]) for color in colors: color_code = self.color_codes.get(color, None) if color_code: node["text"] = f"[color={color_code}]{node['text']}[/color]" return self._handle_text(node) return self._handle_text(node)
def show(self, title, text, fn_ok=None, fn_canc=None, ok_kwargs={}, canc_kwargs={}, **kwargs): self.title = title self.message.text = ''.join( ('[b]', utils.escape_markup(text), '[/b]')) self.fn_ok = fn_ok self.fn_canc = fn_canc self.canc_kwargs = canc_kwargs self.ok_kwargs = ok_kwargs self.open()
def refilter_logs(self): with wlock(): filter_len = len(self.filter_text) new_logs = [] for x in self.log_full: b = x['text0'].lower().find(self.filter_text.lower()) if b != -1: c = b + filter_len start = escape_markup(x['text0'][:b]) end = escape_markup(x['text0'][c:]) # mid = x['text0'][b:c].replace( # self.filter_text, # '[color=%s]%s[/color]' % ( # self.highlight_color, # escape_markup(self.filter_text))) mid = '[color=%s]%s[/color]' % ( self.highlight_color, escape_markup(x['text0'][b:c])) text = ''.join((start, mid, end)) new_logs.append( {'time': x['time'], 'text': text, 'text0': x['text0']}) self.log_filtered = new_logs
def downloadingProgress(self, downloadSessionId, chapterProgress=None, chapterInfo=None, sessionProgress=None, mangaInfo=None, sessionFail=False, downloadCompleted=False): with self.downloadUILock: statusText = "" downloadSession = self.downloadingMangasIds.get(downloadSessionId, None) if downloadSession is not None: if sessionFail: chapterInfotext = "[b][color=ff0000]" + escape_markup("Failed to download Chapter ") + \ "[color=000000]" + escape_markup(chapterInfo) + "[/color]" + \ escape_markup(" Try again by clicking 'Resume' button") + "[/b][/color]" downloadSession['chapterInfotext'] = chapterInfotext statusText = chapterInfotext self.forceRefreshListView(self.ids.downloadList, statusText) return if chapterProgress is not None: downloadSession['chapterProgress'] = chapterProgress if chapterInfo is not None: downloadSession['chapterInfotext'] = chapterInfo statusText = chapterInfo if mangaInfo is not None: downloadSession['mangaInfotext'] = mangaInfo statusText = mangaInfo if sessionProgress is not None: # print "Weeeeeee "+ str(sessionProgress) downloadSession['mangaProgress'] = sessionProgress #If it is not completed, then only accept any status change of complete if not downloadSession['downloadCompleted']: downloadSession['downloadCompleted'] = downloadCompleted self.forceRefreshListView(self.ids.downloadList, statusText) else: print "Got some extraneous progress info - must have been removed already"
def unhighlight_player(self): self._remove_player_info() if not self._bOusted: sColor = 'aaaaaa' sOusted = '' else: sColor = 'ff3333' sOusted = ' (ousted)' if self._sDeck: label = Label( text="[b][color=%s]%s [i](playing %s)%s[/i]" "[/color][/b]" % (sColor, escape_markup( self._sPlayer), escape_markup(self._sDeck), sOusted), markup=True) else: label = Label(text="[b][color=%s]%s [i](unspecfied)%s" "[/i][/color][/b] " % (sColor, escape_markup(self._sPlayer), sOusted), markup=True) self.player.add_widget(label) self._add_turn_info() self.scroll.scroll_y = 1 self._update_game()
def colored(self): """Implement switch colored.""" if self.root.ids.colors.active: if self.results[0][0].isdigit(): result_index: int = int(self.results[self.marker - 1][0]) RegexLexer.tokens = { "root": [(words({ escape_markup(f"{self.results[self.marker - 1][1]}") }), Generic.Heading), (words({ escape_markup( f"{self.results[self.marker - 1][result_index]}") }), Generic.Inserted)] } else: RegexLexer.tokens = {"root": [(r"^.+$", Generic.Error)]} self.root.ids.s_color.text = self.GREEN_COLORED_STR else: RegexLexer.tokens = {"root": []} self.root.ids.s_color.text = self.RED_NOT_COLORED_STR delattr(RegexLexer, "_tokens") self.root.ids.view.lexer = RegexLexer()
def unhighlight_player(self): self._remove_player_info() if not self._bOusted: sColor = 'aaaaaa' sOusted = '' else: sColor = 'ff3333' sOusted = ' (ousted)' if self._sDeck: label = Label( text="[b][color=%s]%s [i](playing %s)%s[/i]" "[/color][/b]" % (sColor, escape_markup(self._sPlayer), escape_markup(self._sDeck), sOusted), markup=True) else: label = Label( text="[b][color=%s]%s [i](unspecfied)%s" "[/i][/color][/b] " % (sColor, escape_markup(self._sPlayer), sOusted), markup=True) self.player.add_widget(label) self._add_turn_info() self.scroll.scroll_y = 1 self._update_game()
def render_text(t, xxxx): cur_f_color, cur_b_color = last_f_color, last_b_color if last_mode & TextMode.REVERSE: cur_f_color, cur_b_color = last_b_color, last_f_color text = ''.join([ '[color=', self._get_color_hex(cur_f_color), ']', escape_markup(t), '[/color]' ]) text_parts.append(text) return self._add_background(t, cur_b_color, xxxx, y - (i + 1) * dy)
def render_text(t, xxxx): cur_f_color, cur_b_color = last_f_color, last_b_color if last_mode & TextMode.REVERSE: cur_f_color, cur_b_color = last_b_color, last_f_color text = ''.join(['[color=', self._get_color_hex(cur_f_color), ']', escape_markup(t), '[/color]']) text_parts.append(text) return self._add_background(t, cur_b_color, xxxx, y - (i + 1) * dy)
def send_message(self, *args): if len(self.text) > 400: popup = MOPopup("Warning", "Message too long", "OK") popup.open() return main_scr = App.get_running_app().get_main_screen() Clock.schedule_once(main_scr.refocus_text) msg = escape_markup(self.text) if not self.message_is_valid(msg): return self.text = "" msg = self.extend_message(msg) if self.message_is_command(msg): self.handle_command(msg) else: user_handler = App.get_running_app().get_user_handler() user_handler.send_message(msg)
def decision(title, text, fn_ok=None, fn_canc=None, ok_kwargs=None, canc_kwargs=None, **kwargs): global decision_popup if not decision_popup: decision_popup = DecisionPopup() text = ''.join(('[b]', utils.escape_markup(text), '[/b]')) decision_popup.title = title decision_popup.pr_message.text = text decision_popup.fn_ok = fn_ok decision_popup.fn_canc = fn_canc decision_popup.ok_kwargs = ok_kwargs decision_popup.canc_kwargs = canc_kwargs decision_popup.open()
def message(title, text, type='i'): global message_popup if not message_popup: message_popup = MessagePopup() text = utils.escape_markup(f'{text}') if type == 'e': text = ''.join(('[b][color=ff0000]', text, '[/color][/b]')) message_popup.pr_image.source = 'data/icons/bug.png' elif type == 'w': text = ''.join(('[b][color=ffff00]', text, '[/color][/b]')) message_popup.pr_image.source = 'data/icons/pen.png' elif type == 'i': text = ''.join(('[b][color=00ff00]', text, '[/color][/b]')) message_popup.pr_image.source = 'data/icons/ok.png' else: pass message_popup.title = title message_popup.pr_message.text = text message_popup.open()
def show(self, title='', text='', type='i'): text = utils.escape_markup(f'{text}') color = [] if type == 'e': _color = [1,0,0,1] text = ''.join(('[color=ff0000]', text, '[/color]')) self.image.source = os.path.join('data', 'icons', 'info_error.png') elif type == 'w': _color = [1,1,0,1] text = ''.join(('[color=ffff00]', text, '[/color]')) self.image.source = os.path.join('data', 'icons', 'info_warning.png') elif type == 'i': _color = [0,1,0,1] text = ''.join(('[color=00ff00]', text, '[/color]')) self.image.source = os.path.join('data', 'icons', 'info_ok.png') else: pass self.title = title self.title_color = _color self.message.text = text self.open()
def send_pm(self): sender = self.username user = App.get_running_app().get_user() self.avatar = Image(source=user.get_char().avatar, size_hint_x=None, width=60) if self.current_conversation is not None: if self.text_box.text != "" and len(self.text_box.text) <= 400: receiver = self.current_conversation.username self.irc.send_private_msg(receiver, sender, self.text_box.text) msg = self.text_box.text if 'www.' in msg or 'http://' in msg or 'https://' in msg: msg = "[u]{}[/u]".format(msg) self.current_conversation.msgs += "{0}: [ref={2}]{1}[/ref]\n".format( sender, msg, escape_markup(msg)) self.pm_body.clear_widgets() self.update_pms() self.previous_line = self.username self.text_box.text = '' Clock.schedule_once(self.refocus_text, 0.1) else: self.text_box.text = ''
def escape(self, msg): return escape_markup(msg)
def display_persian(text): bidi_text = get_display(ar.reshape(text)) return '[font=B-NAZANIN.TTF][b]' + escape_markup(bidi_text) + '[/b][/font]'
def test_escape_markup(self): escaped = escape_markup('Sun [1] & Moon [2].') self.assertEqual(escaped, 'Sun &bl;1&br; & Moon &bl;2&br;.')
def update_event_info(self, specific_events=None): """ This updates the event info in the scrolling view. :param specific_events: a set of events to be specified. Upon no input, will use all found events. :type: list """ updated_live_music_events = specific_events if specific_events else update_live_music_events() self.layout_display_results.clear_widgets() if len(updated_live_music_events[self.live_music_num]) >= 1: for info_list in updated_live_music_events[self.live_music_num]: self._output_string = '' combined_event_info_list = list() for event_property_list in info_list: combined_event_info_list.extend(event_property_list) if len(combined_event_info_list) != 2: event_date = combined_event_info_list[0] event_time = combined_event_info_list[1] event_cost = combined_event_info_list[2] event_artist = combined_event_info_list[3] event_venue = combined_event_info_list[5] event_address = combined_event_info_list[6] event_url = combined_event_info_list[7] for event_detail in [event_artist, event_date, event_time, event_cost, event_venue, event_address, event_url]: cleaned_event_detail = str(event_detail).lower().strip().title() if cleaned_event_detail != '-' and cleaned_event_detail not in self._output_string: self._output_string += "{} \n".format(cleaned_event_detail) artist = self._output_string.split('\n')[0] if info_list != updated_live_music_events[self.live_music_num][-1]: self._output_string += '------------------------------------' label_results = MyLabel( text='[font=Font/Raleway-SemiBold.ttf]' + escape_markup(artist) + '[/font]' + self._output_string[len(artist)::], markup=True, text_size=[Window.width - 10, None], size_hint_y=None, font_size=10, halign='center' ) self.layout_display_results.add_widget(label_results) else: for sentence in combined_event_info_list: if sentence not in self._output_string: self._output_string += sentence + ' ' label_results = MyLabel(text=self._output_string, text_size=[Window.width - 10, None], halign='center') self.layout_display_results.add_widget(label_results) else: self._output_string = 'I am sorry, \nI could not find any events!' label_results = MyLabel(text=self._output_string, text_size=[Window.width - 10, None], halign='center') self.layout_display_results.add_widget(label_results) if self.parent is not None: Animation(opacity=1, duration=0.1).start(self.parent.base_scroll_view)
def format_title(self, str): watchwords = self.watchwords for word in watchwords: if str.startswith(word + ' '): return '[b]' + word + '[/b]' + escape_markup(str[(len(word)-1):])
def display_text(self, msg, user, color, sender): self.is_displaying_msg = True if self.prev_user is not user or (len(self.text) + len(msg) > 240): self.clear_textbox() self.prev_user = user config = App.get_running_app().config if config.getint('display', 'rpg_mode') == 1: char_name = user.get_char().get_display_name() self.char_name.text = char_name else: self.char_name.text = user.username self.msg = msg user.color = color def text_gen(text): for c in text: yield c config = App.get_running_app().config if user.color == 'ffffff' and config.getint('other', 'instant_text') == 0: self.gen = text_gen(self.msg) config = App.get_running_app().config speed = config.getdefaultint('other', 'textbox_speed', 60) self.sfx["ffffff"].volume = self.volume Clock.schedule_interval(self._animate, 1.0 / speed) else: if user.color in self.sfx: App.get_running_app().play_sound(self.sfx[user.color], volume=self.sfx_volume) if user.color != 'rainbow': self.msg = "[color={}]{}[/color]".format(user.color, self.msg) else: self.msg = self.msg.replace("&bl;", "[") self.msg = self.msg.replace("&br;", "]") self.msg = self.msg.replace("&", "&") msg_array = list(self.msg) self.msg = '' # ff5aac if config.getint("other", "suppress_rainbow") == 0: color_spectrum = [ 'ff3333', 'ffa500', 'ffff00', '33cc33', '00adfc', '8b6fba', 'ee82ee' ] else: color_spectrum = [ 'ff8181', 'ffd689', 'ffff89', 'a1e7a1', '86d9ff', 'b6a4d3', 'f093f0' ] y = 0 for x in range(len(msg_array)): if y == 7: y = 0 col = color_spectrum[y] self.msg += "[color={}]{}[/color]".format( col, escape_markup(msg_array[x])) if msg_array[x] != ' ': y = y + 1 self.text = self.msg self.text += " " self.is_displaying_msg = False main_scr = App.get_running_app().get_main_screen() # BLAAAME KIVYYYY main_scr.log_window.add_chat_entry(self.msg, user.username) if sender == "default": main_scr.toolbar.text_col_btn.text = 'color' user.color = 'ffffff' user.colored = False
def handle_data(self, data): self.plan_buffer = ''.join((self.plan_buffer, escape_markup(data)))
def __init__(self, **kwargs): super(AppBody, self).__init__(**kwargs) initialheight = Window.height initialwidth = Window.width newheight = self.bind(height=self.callback_pos) self.bind(width=self.callback_pos) print("new height: ", newheight) print("the height is: ", str(thisheight), "the width is: ", str(thiswidth), "position is: ", str(thisposition)) text28 = 28 # text28_resized = (newheight*text28)/defaultwidth # self.layout = FloatLayout(size=(300, 300)) labeltitle = Label(text="[size=24][b]" + escape_markup("Hangman Game") + "[/b][/size]", markup=True, size_hint=(.4, .1), pos_hint={'x':0.3, 'y':0.9}) labelsubtitle = Label(text="Guess the word!", size_hint=(.4, .1), pos_hint={'x':0.3, 'y':0.83}) imgsource = 'images/hangman-1-dead.png' # self.imageliste = glob.glob('images/*.png') labelblankword = Label(text="[size=28][b]" + escape_markup("_ A _ _ _ _ _ _ _ _ _ _ _ _ _ _ _") + "[/b][/size]", markup=True, size_hint=(.6, .1), pos_hint={'x':0.2, 'y':0.7}) hangmanpic = Image(source=imgsource, size_hint=(.5, .7), pos_hint={'x':-0.02, 'y':0.1}) self.add_widget(hangmanpic) textinput = TextInput(text='', multiline=False, focus=True, size_hint=(.2, .102), pos_hint={'x':0.61, 'y':0.4}) buttonok = Button(text='OK', size_hint=(.2, .1), pos_hint={'x':0.79, 'y':0.4}) labelwrongguesses = Label(text="" + escape_markup("A, B, C") + "", markup=True, size_hint=(.6, .1), pos_hint={'x':0, 'y':0.1}) buttonsettings = Button(text='Settings', size_hint=(.5, .1), pos_hint={'x':0.0, 'y':0}) buttonexit = Button(text='Exit', size_hint=(.5, .1), pos_hint={'x':0.5, 'y':0}) self.add_widget(labeltitle) self.add_widget(labelsubtitle) self.add_widget(labelblankword) self.add_widget(textinput) self.add_widget(buttonok) self.add_widget(labelwrongguesses) self.add_widget(buttonsettings) self.add_widget(buttonexit)